[英]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: 他提出了游戏中两种角色类型的问题,即巫师和战士,规则是:
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 interface
的getter
。 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: Weapon
和Staff
等级保持不变:
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);
}
}
The T
in Player<T>
is the type of weapon that you want the player to use. 该T
的Player<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
成为仅使用Staff
的Player
。 This way, the Wizard
's weaponInventory
list will contain only Staff
s. 这样, Wizard
的weaponInventory
清单列表将仅包含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.