简体   繁体   English

防止将某些对象添加到ArrayList

[英]Prevent certain objects from being added to a ArrayList

I was reading Eric Lippert blog about Wizards and Warriors . 我正在阅读有关巫师和勇士的 Eric Lippert博客。 Interesting read, but I found certain parts hard to understand (not the authors fault, I'm only a beginner in OOP). 有趣的读物,但是我发现某些部分很难理解(不是作者的错,我只是OOP的初学者)。

He presents the problem of two character types within a game, a Wizard and a Warrior, and the rules are: 他提出了游戏中两种角色类型的问题,即巫师和战士,规则是:

  • A warrior can only use a sword. 战士只能使用剑。
  • A wizard can only use a staff 向导只能使用人员

In the blog, he uses a getter / setter in the first part to handle the weapon for the character, but let's change it to an inventory system. 在博客中,他使用getter / setter第一部分来处理人物的武器,但我们将其更改为库存系统。 So, we have an abstract class called Player with a list of items( ArrayList ). 因此,我们有一个名为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)
}

and use it like so: 并像这样使用它:

public class Wizard extends Player {

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

How would you structure the add method to enforce the rule that a Wizard can only use a staff? 您将如何构造add方法以强制执行向导只能使用人员的规则? I thought of calling getClass() or getType() on weapon but those are considered bad practice. 我曾想过在武器上调用getClass()getType() ,但是这些被认为是不好的做法。

The best answer I could come up with was have a String variable called type, and a getter in the Weapon interface . 我能想到的最好的答案是有一个名为type的String变量,以及Weapon interfacegetter During object construction, set the type to sword or staff. 在对象构建期间,将类型设置为剑或五线谱。 However, this doesn't really help, as you could create a sword object, pass in staff as the type, and use it. 但是,这并没有真正的帮助,因为您可以创建剑对象,将staff作为类型传递并使用它。

How would you prevent a sword from being added to the wizards inventory? 您如何防止将剑添加到向导清单中?

How would you structure the add method to enforce the rule that a Wizard can only use a staff? 您将如何构造add方法以强制执行向导只能使用人员的规则?

Either you didn't read the whole series, or you didn't understand its central message. 您可能没有阅读整个系列,或者您不了解其主要信息。 The entire point of that series of articles is to express that there is no good way to structure a method that enforces a restriction on the relationship between subclasses . 该系列文章的全部要点是要表达出, 没有一种好的方法可以构造一种方法来对子类之间的关系施加限制

This is simply a fact about subclass-based OOP; 这仅仅是基于子类的OOP的事实。 there are some things it does not model well, and this is one of them. 在某些方面建模效果不佳,这就是其中之一。 A fundamental principle of many OOP type systems, including that of Java and C#, is Liskov's: that you can always use an instance of a subclass where an instance of a superclass is needed. Liskov是许多OOP类型系统(包括Java和C#)的基本原理:您可以始终使用需要超类实例的子类实例。 That makes modeling restrictions in the type system difficult. 这使得类型系统中的建模限制变得困难。

In the last part of the series I give the best solution that I know of: model the rules of the simulation as objects themselves , and have a rule enforcer class that gives the user feedback when they attempt to violate a rule. 在本系列的最后一部分中,我提供了我所知道的最佳解决方案: 将模拟规则建模为对象本身 ,并具有规则执行器类,该类在用户尝试违反规则时向用户提供反馈。 Don't make the type system try to solve your business problems. 不要让类型系统尝试解决您的业务问题。

You could use something like the following. 您可以使用类似以下的内容。 Note: in the Player class, the weapons can be of any type. 注意:在Player类中,武器可以是任何类型。 However each sub-class of player has its own specific add() . 但是,播放器的每个子类都有其自己的特定add() So while this approach enforces the required rules, it loses a little generality. 因此,尽管此方法强制执行必需的规则,但会失去一些通用性。

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);
     }
 }

You could use generics : 您可以使用泛型

Weapon and Staff classes remain the same: WeaponStaff等级保持不变:

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

public class Staff implements Weapon {

   @Override
   public void attack(Enemy enemy) {
    //Do ur attacking. :)
   }

}

The Player class has a generic type : 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);

}

And the Wizard class extends Player<Staff> (NOT just Player ): Wizard类扩展了Player<Staff> (不仅仅是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);
    }

}

Explanation: 说明:

The T in Player<T> is the type of weapon that you want the player to use. TPlayer<T>是要玩家使用的武器种类。

When you extend Player<Staff> in the Wizard class, you're saying that you want Wizard to be a Player that only uses Staff s. 当您在Wizard类中扩展Player<Staff>时,是说您希望Wizard成为仅使用StaffPlayer This way, the Wizard 's weaponInventory list will contain only Staff s. 这样, WizardweaponInventory清单列表将仅包含Staff

When you add the Warrior class, it would extend Player<Sword> , which would make its weaponInventory only take Sword s. 当您添加Warrior类时,它将扩展Player<Sword> ,这将使其weaponInventory仅使用Sword

By the way, I instantiated weaponInventory in the above code and implemented the add method in Wizard . 顺便说一下,我在上面的代码中实例化了weaponInventory ,并在Wizard实现了add方法。

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

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