【问题标题】:Design pattern to avoid violating Open-Closed principle避免违反开闭原则的设计模式
【发布时间】:2021-12-20 06:06:36
【问题描述】:

我正在用 Java 构建一个简单的游戏。我有几个课程,我省略了与我的问题无关的字段:

public class Character {
    //stores relics and artifacts
    public Set<Collectable> inventory;

    public void collect(Collectable collectable) {
        collectable.collect(this);
    }
}

public class Artifact extends Collectable {
    @Override
    public void collect(Character character) {
        character.inventory.add(this);
    }
}

public class Relic extends Collectable {
    @Override
    public void collect(Character character) {
        character.inventory.add(this);
    }
}

public class Spell extends Collectable {
    @Override
    public void collect(Character character) {
        Wizard wizard = (Wizard) character;
        wizard.spellBook.add(this);
    }
}

public class Wizard extends Character {
    //stores spells
    public Set<Collectable> spellBook;
}

public class Warrior extends Character {
    //fields and methods ommited
}

目前,当我收集法术时,它必须进入巫师的法术书。战士不能收集法术,他们没有法术书。 如果我从 OOP POV 中正确理解,Collectable 必须能够在收集时决定它的去向(库存或法术书),因此我在上面的解决方案。

我的问题是我必须在 Spell.collect(Character) 中使用类型转换才能将 Spell 放入巫师的 spellBook,因为默认情况下,spellBook 在 Character 上不可见,我认为它不应该是,因为那时勇士也会有拼写书。 这违反了开闭原则,因为如果我想添加一个也可以收集法术的术士,我必须修改法术以尝试将它也施放给术士。

您能否提出一个解决方案或设计模式,以便我可以在不违反开闭原则的情况下收集我的收藏品?

【问题讨论】:

    标签: java oop design-patterns solid


    【解决方案1】:

    想想这个很有趣。这里的其他答案肯定已经解决了您的问题,但我认为在您需要将架构更改为 MVC(模型视图控制器)或 SAM(状态动作模型)之类的东西的宏伟计划中。这些将使您更好地了解如何组成类,b/c 现在似乎您正在尝试根据物理对象来建模您的世界,这不是 OOP 的意义所在。 OOP 是关于数据的传输。

    使用 MVC,它可能看起来像:

    型号:

    public class Spell extends MagicCollectable {
        // attributes like damage or healing
    }
    
    public class Relic extends PhysicalCollectable {
        // attributes 
    }
    
    public class Wizard extends Character {
        //stores spells
        public Set<Collectable> spellBook;
    }
    
    public class Warrior extends Character {
        //fields and methods ommited
    }
    

    控制器:

    public class WizardController {
        private Wizard wizard;
        public void collect(MagicCollectable collectable);
    }
    
    public class WarriorController {
        private Warrior warrior;
        public void collect(PhysicalCollectable collectable);
    }
    

    因此,在您的游戏循环中,您实际上会实例化 WizardController 来体现您的角色。另请注意,与其他答案一样,我正在创建更具体的模型。

    【讨论】:

      【解决方案2】:

      public Set&lt;Collectable&gt; spellBook; 没有理由不应该是 public Set&lt;Spell&gt; spellBook;

      最好的办法是制作一个接口:

      interface SpellCaster{
          void addSpell();
          //other methods
      }
      

      并使任何应该能够收集法术的角色都实现此接口。

      编辑: 然后 Spell 中的 collect 方法应该是这样的:

      @Override
      public void collect(SpellCaster character) {
          character.addSpell(this);
      }
      

      虽然您可能应该重命名该方法。你有两个收集方法,它们做的事情完全不同。

      【讨论】:

      • 这不需要声明 Character.collect(SpellCaster 角色)吗?那我的武者就可以调用这个方法了?此外,这个解决方案似乎并没有解决自排序问题。
      • 1.不,Character 类不会改变。 2. 正如@magicmn 指出的那样。最好让角色处理收藏品的添加,而不是收藏品本身。 “排序”不应该在运行时进行,而是在编写代码时进行。
      • 或许可以举一个例子说明你将如何使用 Collectible.collect() 方法?
      【解决方案3】:

      我觉得有趣的是,你决定让收藏品决定它的去向。在现实世界的场景中,角色不会收集收藏品,例如巫师收集法术吗?

      public class Wizard extends Character {
          //stores spells
          private Set<Spell> spellBook;
      
          public void addSpell(Spell spell) {
              spellBook.add(spell);
          }
      }
      

      【讨论】:

      • 是的,但是这样一来,我必须为我创建的每个 Collectable 实现一个新方法。好像收藏品决定了他们想去的地方,我只需要在创建新的收藏品时实现它。这对我来说似乎是一种 oop 方式,但如果你以另一种方式说服我,我会很高兴,我可以放弃这个解决方案。
      • OOP 的做法并不总是写更少的代码。用于ArtifactReliccollect 所做的事情与Spellcollect 完全不同。事实上,我认为战士可以像收集Relic 一样收集Spell,但永远无法将法术添加到他不存在的法术书中。
      猜你喜欢
      • 2020-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-01
      • 2021-08-24
      • 1970-01-01
      相关资源
      最近更新 更多