简体   繁体   English

在这种情况下,有没有办法避免if-else和动态转换?

[英]Is there any way to avoid if-else and dynamic casting in this case?

For example, for a game I have some Skill, which is data object: 例如,对于游戏我有一些技能,这是数据对象:

public interface Skill{
public String getName();
}

public class Attack implements Skill{
    public String getName(){ return "Attack"; }
    public int power;
}

public class Speak implements Skill{
    public String getName(){ return "Speak"; }
    public String speech;
}

To apply the skills during the game, I need some SkillHandler for each corresponding skill: 为了在游戏中应用技能,我需要一些SkillHandler用于每个相应的技能:

public interface SkillHandler{
    public void apply(Skill skill);
}

public class AttackHandler{
    @Override
    public void apply(Skill skill){
        Attack attack=(Attack)skill;
        Player player=Global.getPlayer();
        Enemy enemy=Global.getEnemy();
        enemy.hp=enemy.hp-attack.power;
        //some other code for follow up handle
    }
}

public class SpeakHandler{
    @Override
    public void apply(Skill skill){
        Speak speak=(Speak)skill;
        Label label=new Label(speech);
        this.displayOnTop(label);
    }
}

I use one SkillHandler for each Skill because I don't want the Skill depend on SkillHandler, and PlayerAttackStateHandler would apply each skill: 我为每个技能使用一个SkillHandler,因为我不希望技能依赖于SkillHandler,而PlayerAttackStateHandler会应用每个技能:

public class PlayerAttackStateHandler{
    public PlayerAttackHandler(){
        Skill[] skills=Global.getSkills();
        for(int i=0;i<skills.length;i++){
            SkillHandler skillHandler=null;

            if(skills[i].getName().equals("Attack")){
                skillHandler=new AttackHandler();
            }else if(skills[i].getName().equals("Speak")){
                skillHandler=new SpeakHandler();
            }

            skillHandler.apply(skills[i]);
        }
    }
}

I know this design is ill-formed because it has at least 2 problems: 我知道这个设计格式不正确,因为它至少有两个问题:

  1. I need to update the long if-else chain if a new Skill as well as new SkillHandler is added, which seems does't follow open-closed principle 如果添加了新技能以及新的SkillHandler,我需要更新长if-else链,这似乎不遵循开闭原则

  2. it has a dynamic cast in each SkillHandler 它在每个SkillHandler中都有一个动态转换

My question is, is there any design pattern to eliminate both if-else and dynamic cast (if possible) in this case, while keep the Skill not depend on SkillHandler? 我的问题是,在这种情况下,是否有任何设计模式可以消除if-else和动态转换(如果可能),同时保持技能不依赖于SkillHandler?

It seems that your implementation has lot in common with visitor pattern, something like this: 您的实现似乎与访问者模式有很多共同之处,如下所示:

public interface ISkillable //this is your Skill
{
    public int GetPower();
    public string GetSpeak();
}

public interface IVisitable //player or npc
{
    public void Accept(IVisitor visitor)
}

public interface IVisitor //AttackHandler or SpeakHandler
{
    public void ApplySkill(ISkillable skillable)
}

public class Player implements ISkillable, IVisitable
{
    ...
    public void Accept(IVisitor visitor)
    { 
        visitor.Visit(this); 
    }
}

public class AttackVisitor implements IVisitor
{
    public void Visit(ISkillable skillable)
    {
        //do something with power
    }
}

and then example how it can be used is 然后举例说明如何使用它

player.Accept(new AttackVisitor(/*you can provide additional info like enemy*/));
player.Accept(new SpeakVisitor());

See Attack and Speak as capabilities some agent could possess. 将攻击和说话视为某些代理人可能拥有的能力

I would consider testing capabilities/features: 我会考虑测试功能/功能:

interface Attacking { void attack(); }
interface Speaking { void speak(); }

Animal animal = ...

Optional<Attacking> attacker = animal.lookup(Attacking.class);
attacker.ifPresent(a -> a.attack());

Optional<Speaking> speaker = animal.lookup(Speaking.class);
speaker.ifPresent(sp -> sp.speak());

Animal need not implement any interface, but you can look up (lookup or maybe as) capabilities. Animal不需要实现任何接口,但您可以查找(查找或可能)功能。 This is extendible in the future, dynamic: can change at run-time. 这在未来是可扩展的,动态的:可以在运行时更改。

Implementation as 实施为

private Map<Class<?>, ?> map = new HashMap<>();

public <T> Optional<T> lookup(Class<T> type) {
     Object instance = map.get(type);
     if (instance == null) {
         return Optional.empty();
     }
     return Optional.of(type.cast(instance));
}

<S> void register(Class<S> type, S instance) {
    map.put(type, instance);
}

The implementation does a safe dynamic cast, as register ensures the safe filling of (key, value) entries. 该实现执行安全的动态转换,因为寄存器确保安全填充(键,值)条目。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM