使用策略模式避免向下转换

时间:2017-10-20 02:40:21

标签: java inheritance design-patterns strategy-pattern liskov-substitution-principle

我正在阅读关于利斯科夫替代原则的this site。它声明:

  

根据LSP,使用对基类的引用的函数必须是   能够在不知道的情况下使用派生类的对象。简单来说   单词,派生类必须可替代基类。

根据this page,如果您覆盖基类中的方法并且它什么也不做或抛出异常,那么您违反了该原则。

假设我有abstract class名为Weaponsubclasses ReloadableWeaponSwordReloadableWeapon包含一个对该类唯一的方法,称为Reload()。在声明对象时,标准练习是从抽象类开始,然后是子类,如下所示:

Weapon rifle = new ReloadableWeapon();
Weapon sword = new Sword();

如果我想对method使用重新加载rifle,我可以cast。根据众多文章和教科书,这可能会导致以后出现问题。

另外,如果我在基类Weapon中有重载方法,那么Sword会忽略或抛出,这是错误的。

如果我想避免这一切,那么使用Strategy Pattern是否可行?像这样:

public final Weapon{

    private final String name;
    private final int damage;
    private final List<AttackStrategy> validactions;
    private final List<Actions> standardActions;

    private Weapon(String name, int damage, List<AttackStrategy> standardActions, List<Actions> attacks)
    {
        this.name = name;
        this.damage = damage;
        standardActions = new ArrayList<Actions>(standardActions);
        validAttacks = new ArrayList<AttackStrategy>(validActions);
    }

    public void standardAction(String action){} // -- Can call reload or aim here.  

    public int attack(String action){} // - Call any actions that are attacks. 

    public static Weapon ReloadableWeapon(String name, int damage){
        return new Weapon(name, damage, this.constructActions(), this.constructStandardActions);
    }

    public static Weapon Sword(String name, damage){
        return new Weapon(name, damage, this.standardSwordActions, this.swordActions);
    }

    //returns a List collection that contains the actions for a reloadable Weaopon. - Shoot 
    private List<AttackStrategy> reloadableActions(){}

    //returns a List collection of standard non attack actions. - Reload 
    private List<Actions> standardReloadableActions(){}

     //returns a List collection that contains the actions for a Sword - Swing/Strike  
    private List<AttackStrategy> swordActions(){}

    //returns a List collection of standard non attack actions. - Sharpen 
    private List<Actions> standardSwordActions(){}

}

攻击接口和实施:

public interface AttackStrategy{
    void attack(Enemy enemy);
}

public class Shoot implements AttackStrategy {
    public void attack(Enemy enemy){
        //code to shoot
    }
}

public class Strike implements AttackStrategy {
    public void attack(Enemy enemy){
        //code to strike
    }
}

通过在Weapon类中构造List<AttackStrategy>,客户端代码无法传递List<AttackStrategy>,而不是某些类型的Weapons,例如,剑如果我加了一枚手榴弹,就不能射出子弹了,你就不能射出子弹了(你明白了)。

我不是在问我是否正确实施了Strategy Pattern,而是在遇到pattern时遇到subclass时可以使用method subclass 1}} cast独有,我不想Strategy Pattern它?换句话说,我可以禁止使用继承并使用List<Weapon>来实现require方法,而不是违反LSP吗?

备注:
该模式以两种方式解决了我的问题:

  1. 我不必垂头丧气,我可以将我的武器存放在casting集合中而不用担心检查类型,然后weapon
  2. 任何不是Reloadable的{​​{1}},都没有concrete class Reload。这意味着没有throwing或将method留空
  3. 我的问题是,鉴于策略模式的definition,我不相信我会在这种情况下使用它。

    来自网站:

      

    策略模式将用于您想要选择的位置   在运行时使用的算法。   ...

         

    策略模式提供了一种定义算法族的方法,   将每一个封装为一个对象,并使它们可以互换。

    我喜欢这种方法,我是否可以将其视为允许客户选择如何在Weapon使用runtime,并增加immutability的好处,并避免使用cast?或者我只是将定义拉得太远了?

3 个答案:

答案 0 :(得分:0)

使用策略你会遇到同样的“问题”,当你调用reloadStrategy时,具体策略将是“重新加载”或“不重新加载”。

从第一个设计模式书中看this image:无法飞行的鸭子实现了一个代表他们无法飞行的行为。

答案 1 :(得分:0)

在你的问题中你写道:

  

另外,如果我在基类Weapon中有重载方法,那么Sword会忽略或抛出,这是错误的。

在我看来,这不应该是错误的行为,而应该是正确的行为 您可以将武器视为接口,并在 ReloadableWeapon NonReloadableWeapon 类中,实现您希望在武器类中显示的方法,assignignig to对于与特定情况不匹配的方法的“无操作”行为(例如 NonReloadableWeapon 中的重新加载方法在其实现中将为空,或者更好,它将发布例外) 另一个选择是使用默认方法(Java 8)在Weapon类中实现启动异常的默认行为;将在有意义的子类中重写的方法 在这种情况下:
  1。您将维持替代性原则
  2。您可以以其他方式使用继承(例如,如果您希望武器作为更通用的类项的子类)
  3。你不会被迫使用istanceOf或cast

结束,在我看来,Stategy模式可能对你的环境有用,可以改变使用你的武器的客户类的行为,但它不能解决缺乏可替代性的问题。

答案 2 :(得分:0)

鉴于该行代码

public void standardAction(String action){} // -- Can call reload or aim here. 

两件事之一 -

  • 调用者是否知道他们的武器是否需要重新加载,这就是 战略毫无用处。

  • 或者他们不知道它并且它可能是尝试重新加载不可重新加载的东西的正常例程的一部分,在这种情况下sword.Reload() { // do nothing }选项是完全有效的。

只有上下文可以告诉你哪个是真的。定义调用者是谁,并定义攻击的行为意味着什么,你会更清楚地看到你需要去的地方。