简体   繁体   English

在这种情况下如何避免instanceof运算符

[英]How to avoid instanceof operator in this case

I'm writing simple game with cells which can be in two states Free and Taken by player. 我正在编写简单的游戏,其中的细胞可以处于两种状态,由玩家自由 拍摄

interface Cell {
    int posX();
    int posY();
}

abstract class BaseCell implements Cell {

    private int x;
    private int y;

    public int posX() {
        return x;
    }

    public int posY() {
        return y;
    }

    ...
}

class FreeCell extends BaseCell {
}

class TakenCell extends BaseCell {
    private Player owningPlayer

    public Player owner() {
        return owningPlayer;
    }

}

In each turn I need to inspect all cells to calculate next cell state with method as below 在每个回合中,我需要检查所有细胞以使用如下方法计算下一个细胞状态

// method in class Cell
public Cell nextState(...) {...}

and collect (in Set ) all cells that are not yet taken. 并收集(在Set )尚未拍摄的所有细胞。 The method above returns Cell because cell may change from Free to Taken or the opposite. 上面的方法返回Cell因为单元格可能会从Free变为Taken或相反。 I'm doing something like below to collect them: 我正在做类似下面的事情来收集它们:

for (Cell cell : cells) {
    Cell next = cell.futureState(...);
    if(next instanceof FreeCell) {
        freeCells.add(currentCell);
    }
    ...
}

It's ugly. 它很丑。 How to do that to avoid such instanceof hacks? 如何做到这一点,以避免这样的hacks实例? I'm not talking about another hack, but would like to find out proper OOP solution. 我不是在谈论另一个hack,而是想找出适当的OOP解决方案。

It sounds like you are flirting with the "State" pattern but you are not quite there. 听起来你正在调情“状态”模式,但你并不完全在那里。 Using the state pattern you would have your Cell object and a hierarchy of "Cell State" classes. 使用状态模式,您将拥有Cell对象和“Cell State”类的层次结构。

The Cell object would use composition rather than inheritance. Cell对象将使用组合而不是继承。 In other words, a Cell would have a current state property. 换句话说,Cell将具有当前状态属性。 When you have a Cell where the currentState property is a FreeState object then it's a free cell. 如果你有一个Cell,其中currentState属性是一个FreeState对象,那么它就是一个空闲单元格。 When you have a Cell where the currentState property is a TakenState object, then it's a free state. 如果你有一个Cell,其中currentState属性是TakenState对象,那么它是一个自由状态。

How to do that to avoid such instanceof hacks? 如何做到这一点,以避免这样的hacks实例?

Whenever you have a situation where you would need to do an instanceof, you add a method to your Cell class and just invoke it. 每当您遇到需要执行instanceof的情况时,都会向Cell类添加一个方法并调用它。 The Cell delegates to the current state. Cell委托给当前状态。 The code in Cell which delegates to the current state does not actually know what the state is. 委托给当前状态的Cell中的代码实际上并不知道状态是什么。 It just trusts that the state will do the right thing. 它只相信国家会做正确的事情。 In your FreeState and TakenState you provide implementations of each method that do the right thing based on their state. 在FreeState和TakenState中,您提供了每种方法的实现,这些方法根据其状态执行正确的操作。

I think the design problem here is that you have two different classes for what can be essentially two different states of the same cell. 我认为这里的设计问题是你有两个不同的类,它们基本上可以是同一个单元的两个不同状态。

What do you do now when a previously free cell becomes occupied? 当以前的免费细胞被占用时你现在做什么? Create a new object with same coordinates and discard the old one? 创建一个具有相同坐标的新对象并丢弃旧的对象? But it's still the same cell conceptually! 但它在概念上仍然是相同的细胞! (Or can there be a free cell and a taken cell with same x and y at the same time?) (或者可以同时存在一个空闲单元格和一个具有相同x和y的单元格?)

From the OOP perspective, you should have one cell class with an attribute "taken", or as another anwer suggests, "owner information". 从OOP的角度来看,您应该有一个具有属性“take”的单元类,或者另一个anwer建议的“所有者信息”。 If you feel that this should not be part of the cell class for whatever reason, what about keeping the owner information separate in a Map<Cell,Owner> ? 如果您认为这不应该是因为某种原因而不属于单元类的一部分,那么如何在Map<Cell,Owner>保持所有者信息分开呢?

Okay, here's another approach you can take. 好的,这是你可以采取的另一种方法。

 public class Cell {

     private int x;
     private int y;
     private OccupationInfo occupationInfo;

     public int posX() {
         return x;
     }

     public int posY() {
        return y;
     }

     public OccupationInfo getOccupationInfo() {
        return occupationInfo;
     }

     public boolean isFree() {
        return occupationInfo == null;
     }
  }

And then... 接着...

  public class OccupationInfo {
      private Player owningPlayer;
      // any other data you would've put in `TakenCell`
  }

This may or may not be good for your exact purposes but it's a clean and simple design. 这可能会或可能不会对您的确切目的有利,但它是一个简洁的设计。

I think it's good place to use Factory Pattern or Abstract Factory Pattern . 我认为这是使用Factory PatternAbstract Factory Pattern的好地方。

Factory pattern returns an instance of several (product hierarchy) subclasses (like FreeCell , TakenCell etc), but the calling code is unaware of the actual implementation class. 工厂模式返回多个(产品层次结构)子类的实例(如FreeCell,TakenCell等),但调用代码不知道实际的实现类。
The calling code invokes the method on the interface for example FreeCell and using polymorphism the correct doSomething() method gets invoked. 调用代码调用接口上的方法,例如FreeCell并使用多态,调用正确的doSomething()方法。

Instead to use instanceof (like switching) you just might invoke the same method but each class will implement it according to local overriding. 而不是使用instanceof (如切换),您可能只调用相同的方法,但每个类将根据本地覆盖实现它。 This is a very powerful and common feature in many frameworks. 这是许多框架中非常强大和通用的功能。

Instead to write: 而不是写:

for (Cell cell : cells) {
Cell next = cell.futureState(...);
if(next instanceof FreeCell) {
    freeCells.add(currentCell);
}
...
}

You can type: 你可以输入:

for (Cell cell : cells) {
Cell next = cell.futureState(...);
 cell.doSomething(); // and no matter what class is FreeCell or TakenCell 
...

} }

Factory pattern returns one of the several product subclasses. 工厂模式返回多个产品子类中的一个。 You should use a factory pattern If you have a super class and a number of subclasses, and based on some data provided, you have to return the object of one of the subclasses. 您应该使用工厂模式如果您有超类和许多子类,并且基于提供的某些数据,您必须返回其中一个子类的对象。

在此输入图像描述

Links: 链接:

Abstract Factory pattern 抽象工厂模式

Factory pattern 工厂模式

You can add method to Cell interface that will tell whether the cell is free: 您可以向Cell接口添加方法,以告知该单元格是否空闲:

interface Cell {
    int posX();
    int posY();
    boolean isFree();
}

class FreeCell extends BaseCell {
    public boolean isFree() { return true; }
}

class TakenCell extends BaseCell {
    private Player owningPlayer

    public boolean isFree() { return false; }

    public Player owner() {
        return owningPlayer;
    }
}

But I don't think this is much better than using instanceof 但我不认为这比使用instanceof好多了

Can you have two Sets and move the cells from one to the other when they are taken? 你有两个套装,并在拍摄时将细胞从一个移动到另一个吗? For example at the beginning you would have the freeSet full of cells and the takenSet empty. 例如,在开头你会有freeSet充满单元格而takenSet空。 As cells are taken they are moved from freeSet to takenSet. 当细胞被采集时,它们从freeSet移动到takeSet。 If you have an interface above TakenCell and FreeCell you could type each set with the same interface. 如果你有一个TakenCell和FreeCell之上的接口,你可以用相同的接口键入每个集合。

Alternatively... 另外...

It would be helpful to see your definition of FreeCell and TakenCell but I would think you could model them as the same object with a nullable field that becomes populated to indicate that it is Taken. 看看你对FreeCellTakenCell定义会很有帮助,但是我认为你可以将它们建模为同一个对象,并且可以填充一个可以为空的字段来表明它是Taken。 You could then use two sets types to the same class. 然后,您可以对同一个类使用两种类型。

Your code isn't ugly, it reads well, it clearly expresses the application logic. 你的代码并不难看,它读得很好,它清楚地表达了应用程序逻辑。

A single instanceof test usually isn't something to worry about; 单个测试instanceof通常不值得担心; its use is common. 它的用途很常见。 "Marker interfaces" are tested by instanceof . “标记接口”由instanceof测试。 Your example is kind of a marker interface. 你的例子是一种标记界面。

And instanceof is insanely fast, apparently JVM finds it worthwhile to optimize it very well. 而且instanceof非常快,显然JVM发现它非常适合优化它。

However, a chain of instanceof tests can be a sign of problem. 但是,一系列测试instanceof可能是问题的标志。 Add a guard to make sure the enumeration is complete. 添加一个警卫以确保枚举完成。

if(o instanceof A)
    ...
else if(o instanceof B)
    ...
else if ...
...
else // huh? o is null or of unknown type
    throw new AssertionError("unexpected type: "+o); 

you can use Visitor Pattern , these FreeCell and TakenCell should implement 你可以使用访客模式 ,这些FreeCell和TakenCell应该实现

Visitable interface 可访问的界面

interface Visitable {
    void accept(Visitor visitor);
}

interface Visitor {
    void visit(FreeCell freeCell);
    void visit(TakenCell takenCell);
}

in the implementation of Visitor's visit(FreeCell freecCell) method will be: 在访问者访问(FreeCell freecCell)方法的实现将是:

public void visit(FreeCell freeCell) {
    freeCells.add(freeCell);
}

in the implementation of Visitor's visit(TakenCell takenCell) method will be nothing 在访问者访问(TakenCell采取细胞)方法的实现将无所作为

and both classes: FreeCell and TakenCell, in method accept(Visitor visitor) should have: 和两个类:FreeCell和TakenCell,方法接受(访问者访问者)应该具有:

public void accept(Visitor visitor) {
    visitor.visit(this);
}

and in for loop you should have: 在for循环中你应该有:

for (Cell cell : cells) {
Cell next = cell.futureState(...);
next.accept( someConcreteVisitor )
...
}

someConcreteVisitor is the Visitor's implementer's instance. someConcreteVisitor是Visitor的实现者的实例。

the class, where this for loop is, can be Visitable too. 这个for循环所在的类也可以是Visitable。

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

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