Java 摆脱“instanceof”`
在我写的一个基于精灵的游戏中,2D网格中的每个区域都包含一堆精灵。最重要的一点 在游戏的规则模块中,我有很多这样的代码:Java 摆脱“instanceof”`,java,types,sprite,instanceof,Java,Types,Sprite,Instanceof,在我写的一个基于精灵的游戏中,2D网格中的每个区域都包含一堆精灵。最重要的一点 在游戏的规则模块中,我有很多这样的代码: public boolean isGameWon(Board board) { for (Point point : board.getTargetPoints()) if(!(board.getTopSpriteAt(point) instanceof Box)) return false; return true;
public boolean isGameWon(Board board) {
for (Point point : board.getTargetPoints())
if(!(board.getTopSpriteAt(point) instanceof Box))
return false;
return true;
}
class Sprite {
public int doSomething(int cnt){
return cnt;
}
}
class Box extends Sprite {
@Override
public int doSomething(int cnt){
return cnt + 1;
}
}
Upadate:
//如果在每个目标的顶部有一个框
,则“做点什么”
很重要。我不知道如何简单地将doSomething()
添加到Sprite中,除非doSomething()
如果Sprite是一个长方体,则返回1,否则返回0。(这与instanceof完全相同)
我知道instanceof被认为是有害的,因为它扼杀了面向对象编程的思想
但是,我不确定如何修复我的案例中的代码。以下是我的一些想法:
- 我不认为简单地向
Sprite
界面添加isABox()
方法会更好
- 如果
Box
是一个接口,那么其他类可以获得相同的特权,这会有帮助吗
- 我是否应该尝试使用类似访问者的模式进行模式匹配/双重分派之类的花式操作
- 规则模块与类型密切合作是否可以,仅仅因为它应该知道它们的语义
- 规则模块策略模式的整个理念是否有缺陷
- 将规则构建到精灵中是没有意义的,因为当添加新类型时,它们都必须更改
我希望您尝试过类似的方法,并能为我指出正确的方向。使用:
因此,您只需要在示例中调用sprite.someMethod()。关于面向对象设计/重构的一般性陈述很难给出IMHO,因为“最佳”操作在很大程度上取决于上下文
您应该尝试将“做点什么”移动到一个虚拟的Sprite方法中,该方法什么都不做。可以从循环中调用此方法
然后,Box可以覆盖它并执行“something”。基本重载就是一种方法。Sprite类层次结构应该知道做什么以及如何做,如:
interface Sprite {
boolean isCountable();
}
class MyOtherSprite implements Sprite {
boolean isCountable() {
return false;
}
}
class Box implements Sprite {
boolean isCountable() {
return true;
}
}
int count = 0;
for (Point point : board.getTargetPoints()) {
Sprite sprite = board.getTopSpriteAt(point);
count += sprite.isCountable() ? 1 : 0;
}
编辑:
您对问题的编辑不会从根本上改变问题。您拥有的是一些仅适用于Box
的逻辑。同样,将该特定逻辑封装在框
实例中(见上文)。您可以更进一步,为您的精灵创建一个通用超类,为isCountable()
定义一个默认值(请注意,该方法类似于isBox方法,但实际上从设计角度来看更好,因为圆有isBox方法没有意义-Box是否也应该包含isCircle方法?).当你说“堆叠”和“最上面的一个很重要”时,你能不能不拿最上面的一个做点什么?基本上,而不是
if (sprite instanceof Box)
// Do something
使用
其中doSomething()
在Sprite
中定义,并在框中覆盖
如果希望将这些规则从Sprite类层次结构中分离出来,可以将它们移动到一个单独的rules
类(或接口),其中Sprite
有一个getRules()
方法,子类返回不同的实现。这将进一步增加灵活性,因为它允许相同的Sprite
子类的对象具有不同的行为。我认为人们向您提出的建议是正确的。您的doSomething()
可以如下所示:
public boolean isGameWon(Board board) {
for (Point point : board.getTargetPoints())
if(!(board.getTopSpriteAt(point) instanceof Box))
return false;
return true;
}
class Sprite {
public int doSomething(int cnt){
return cnt;
}
}
class Box extends Sprite {
@Override
public int doSomething(int cnt){
return cnt + 1;
}
}
因此,在原始代码中,可以执行以下操作:
int cnt = 0;
for (Point point : board.getTargetPoints()) {
Sprite sprite = board.getTopSpriteAt(point)
cnt = sprite.doSomething(cnt);
}
或者,您也可以按照建议实现相同的目标,但每个循环可能需要额外的计算
class Sprite {
public boolean isBox() {
return false;
}
}
class Box extends Sprite {
@Override
public boolean isBox(){
return true;
}
}
下面是一个没有isAnX()方法的泛型计数器的示例,用于您可能要计数的每种类型。假设您要计算板上X型的数量
public int count(Class type) {
int count = 0;
for (Point point : board.getTargetPoints())
if(type.isAssignable(board.getTopSpriteAt(point)))
count++;
return count;
}
我怀疑你真正想要的是
public boolean isAllBoxes() {
for (Point point : board.getTargetPoints())
if(!board.getTopSpriteAt(point).isABox())
return false;
return true;
}
这是我的尝试。考虑用不同的精灵类型定义枚举:
class Sprite {
public enum SpriteType {
BOX, CAT, BOTTLE, HUMAN, TARGET, /* ... */, SIMPLE;
}
public SpriteType getSpriteType(){
return SIMPLE;
}
}
class Box extends Sprite {
@Override
public SpriteType getSpriteType(){
return Sprite.SpriteType.BOX;
}
}
最后:
public boolean isGameWon(Board board) {
for (Point point : board.getTargetPoints())
if(board.getTopSpriteAt(point).getSpriteType()!=SpriteType.BOX)
return false;
return true;
}
这样,您就可以解决必须在Sprite中为每个类型X创建isATypeX()方法的问题。
如果您需要一个新类型,则向枚举添加一个新值,并且只有需要检查此类型的规则才需要确认它。您在这里真正测试的是
玩家能用这个精灵在棋盘顶部赢得游戏吗
因此,我建议使用以下名称:
public boolean isGameWon(Board board) {
for (Point point : board.getTargetPoints())
if(!board.getTopSpriteAt(point).isWinningSprite())
return false;
return true;
}
拥有isBox
功能绝对没有意义。一点也没有。您不妨使用instanceof
但是如果Box
、瓶子
和Target
都是赢家,那么您可以让他们全部返回
class Box {
public override bool isWinningSprite() { return true; }
}
然后,您可以添加另一种类型的“获胜”精灵,而无需更改isGameWon
功能。
:(几乎)总是有害的
我看了你帖子的所有答案,试图理解你在做什么。我得出的结论是,instanceof
正是您想要的,并且您的原始代码示例很好
你澄清说:
- 您没有违反Liskov替换原则,因为没有任何方块代码使精灵代码无效
- 您是而不是用对
instanceof
的响应来分叉代码。这就是为什么人们说instanceof不好;因为人们这样做:
if(shape instanceof Circle) {
area = Circle(shape).circleArea();
} else if(shape instanceof Square) {
area = Square(shape).squareArea();
} else if(shape instanceof Triangle) {
area = Triangle(shape).triangleArea();
}
这就是为什么人们避免使用instanceof的原因。但这不是你正在做的
Box
和赢得游戏之间存在一对一的关系(没有其他精灵可以赢得游戏)。因此,您不需要额外的“赢家”精灵抽象(因为box==Winners)
- 您只需检查电路板,以确保每个最上面的项目都是一个方框。这正是
instanceof
的设计目的
boolean acceptBoxDetection(Visitor boxDetector){
return boxDetector.visit(this);
}
and then Visitor does:
boolean visit(Box box){
return true;
}
boolean visit(OtherStuff other){
return false;
}