【问题标题】:Prevent certain objects from being added to a ArrayList防止某些对象被添加到 ArrayList
【发布时间】:2018-03-15 07:06:51
【问题描述】:

我正在阅读关于 Wizards and Warriors 的 Eric Lippert 博客。有趣的阅​​读,但我发现某些部分难以理解(不是作者的错,我只是 OOP 的初学者)。

他提出了一个游戏中两种角色类型的问题,一个巫师和一个战士,规则是:

  • 战士只能使用剑。
  • 法师只能使用法杖

在博客中,他在第一部分使用getter/setter 来处理角色的武器,但我们将其更改为库存系统。所以,我们有一个名为 Player 的 abstract class 和一个项目列表 (ArrayList)。

interface Weapon {
  attack(Enemy enemy);
}

public class Staff implements Weapon {} 

public abstract class Player {
    private List<Weapon> weaponInventory;

    //left out constructor and other methods to keep it on point

    abstract void add(Weapon add)
}

并像这样使用它:

public class Wizard extends Player {

   @Override
   public void add(Weapon add){
      //code to add weapon;
   }
}

您将如何构造 add 方法来强制执行向导只能使用五线谱的规则?我想过在武器上打电话给getClass()getType(),但这些都被认为是不好的做法。

我能想到的最佳答案是有一个名为 type 的 String 变量,以及在 Weapon interface 中的 getter。在对象构造期间,将类型设置为剑或法杖。但是,这并没有真正的帮助,因为您可以创建一个剑对象,将工作人员作为类型传入,然后使用它。

你会如何防止剑被添加到巫师的库存中?

【问题讨论】:

  • 这在很大程度上取决于您的设计。如果您希望在编译时完成检查,那么答案之一中建议的泛型会有所帮助。然而,在更现实的设计中,您很少将Staff 显式地赋予Wizard。相反,您将Weapon 提供给Player。所以检查必须在运行时完成,泛型在这里无济于事。所以在不知道你的设计的情况下很难给你答案
  • @AdrianShum - 假设运行时
  • 刚刚快速浏览了这篇文章,作者正在考虑编译时...
  • @AdrianShum - 在这种情况下不喜欢使用泛型。对于我的问题,我想知道如何在运行时防止巫师将剑添加到库存中。他使用 getter/setter,但实际上您将使用系统(列表、字典等)之类的库存
  • @AdrianShum - 这不会带来对客户过于信任的问题吗?是什么阻止他们在 Sword 构造函数中放置 WeaponType.Staff?除非你自己做,即客户端没有设置它,你在构造函数中私下做。

标签: java oop arraylist


【解决方案1】:

您将如何构造 add 方法来强制执行向导只能使用五线谱的规则?

要么您没有阅读整个系列,要么您不了解它的中心信息。该系列文章的全部意义在于表达没有很好的方法来构造一个强制限制子类之间关系的方法

这只是关于基于子类的 OOP 的一个事实;有些东西不能很好地建模,这就是其中之一。许多 OOP 类型系统(包括 Java 和 C#)的基本原则是 Liskov 的:您可以始终在需要超类实例的情况下使用子类的实例。这使得类型系统中的建模限制变得困难。

在本系列的最后一部分中,我给出了我所知道的最佳解决方案:将模拟规则建模为对象本身,并有一个规则执行器类,当用户使用它们时提供反馈试图违反规则。不要让类型系统试图解决你的业务问题。

【讨论】:

    【解决方案2】:

    你可以使用泛型

    WeaponStaff 类保持不变:

    public interface Weapon {
       void attack(Enemy enemy);
    }
    
    public class Staff implements Weapon {
    
       @Override
       public void attack(Enemy enemy) {
        //Do ur attacking. :)
       }
    
    }
    

    Player 类有一个通用类型

    import java.util.ArrayList;
    import java.util.List;
    
    public abstract class Player<T extends Weapon> {
        protected List<T> weaponInventory = new ArrayList<>();//Made protected so Wizard can access it.
    
        public abstract void add(T weapon);
    
    }
    

    Wizard 类扩展了Player&lt;Staff&gt;(不仅仅是Player):

    public class Wizard extends Player<Staff> {
    
        @Override
        public void add(Staff weapon) {
            // Add the staff to the list declared in Player
            weaponInventory.add(weapon);
        }
    
    }
    

    解释:

    Player&lt;T&gt; 中的 T 是您希望玩家使用的武器类型。

    当您在Wizard 类中扩展Player&lt;Staff&gt; 时,您是说您希望Wizard 成为仅使用Staffs 的Player。这样,WizardweaponInventory 列表将只包含Staffs。

    当您添加Warrior 类时,它将扩展Player&lt;Sword&gt;,这将使其weaponInventory 只占用Swords。

    顺便说一下,我在上面的代码中实例化了weaponInventory,并在Wizard中实现了add方法。

    【讨论】:

      【解决方案3】:

      您可以使用以下内容。注意:在Player 类中,武器可以是任何类型。然而,每个玩家子类都有自己特定的add()。因此,虽然这种方法强制执行所需的规则,但它失去了一点通用性。

      public class Staff implements Weapon {} 
      public class Sword implements Weapon {} 
      
      public abstract class Player {
          private List<Weapon> weaponInventory;
          protected final void addWeapon(Weapon weapon) {
               weaponInventory.add(weapon)
          }
       }
      
       public class Wizard extends Player {
           public void add(Staff staff) {
               addWeapon(staff);
           }
       }
      
       public class Warrior extends Player {
           public void add(Sword sword) {
               addWeapon(sword);
           }
       }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-12-20
        • 1970-01-01
        • 2023-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多