Java 摆脱“instanceof”`

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;

在我写的一个基于精灵的游戏中,2D网格中的每个区域都包含一堆精灵。最重要的一点

在游戏的规则模块中,我有很多这样的代码:

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