4

私は、プレイヤーがFreeTakenの 2 つの状態になることができるセルを使用した単純なゲームを書いています。

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

}

各ターンで、すべてのセルを検査して、以下の方法で次のセル状態を計算する必要があります

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

Setまだ取得されていないすべてのセルを( で) 収集します。Cellセルが Free から Taken に、またはその逆に変化する可能性があるため、上記のメソッドは戻ります。私はそれらを収集するために以下のようなことをしています:

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

それは醜いです。そのようなインスタンスのハックを回避するためにそれを行う方法は? 別のハックについて話しているわけではありませんが、適切な OOP ソリューションを見つけたいと思います。

4

8 に答える 8

5

あなたは「状態」パターンをいじっているように聞こえますが、そこまでではありません。状態パターンを使用すると、Cell オブジェクトと「Cell State」クラスの階層が作成されます。

Cell オブジェクトは、継承ではなく構成を使用します。つまり、セルには現在の状態プロパティがあります。currentState プロパティが FreeState オブジェクトである Cell がある場合、それはフリー セルです。currentState プロパティが TakenState オブジェクトであるセルがある場合、それは自由な状態です。

そのようなインスタンスのハックを回避するためにそれを行う方法は?

instanceof を実行する必要がある場合はいつでも、Cell クラスにメソッドを追加して呼び出すだけです。Cell は現在の状態に委譲します。現在の状態に委譲する Cell のコードは、実際には状態が何であるかを知りません。国が正しいことをしてくれると信じているだけです。FreeState と TakenState では、状態に基づいて正しいことを行う各メソッドの実装を提供します。

于 2012-11-16T22:14:01.503 に答える
4

ここでの設計上の問題は、同じセルの本質的に 2 つの異なる状態になる可能性があるものに対して、2 つの異なるクラスがあることだと思います。

以前は空いていたセルが占有されたとき、あなたは今何をしますか? 同じ座標で新しいオブジェクトを作成し、古いオブジェクトを破棄しますか? しかし、概念的には同じセルです。(または、同じ x と y を持つフリー セルとテイク セルが同時に存在する可能性はありますか?)

OOPの観点からは、属性が「取得」されたセルクラス、または別の回答が示唆するように「所有者情報」を持つ1つのセルクラスが必要です。なんらかの理由でこれを cell クラスの一部にするべきではないと思われる場合は、所有者情報をMap<Cell,Owner>?

于 2012-11-16T22:13:28.230 に答える
3

さて、ここであなたが取ることができる別のアプローチがあります。

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

その後...

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

これは、正確な目的に適している場合とそうでない場合がありますが、クリーンでシンプルなデザインです。

于 2012-11-16T22:06:43.623 に答える
1

セルが空いているかどうかを伝えるメソッドを 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;
    }
}

しかし、これは instanceof を使用するよりもはるかに優れているとは思いません

于 2012-11-16T22:07:07.560 に答える
1

Factory PatternAbstract Factory Patternを使用するのに適した場所だと思います。

ファクトリ パターンは、いくつかの (製品階層) サブクラス ( FreeCell 、 TakenCell など) のインスタンスを返しますが、呼び出しコードは実際の実装クラスを認識しません。
たとえば、呼び出しコードはインターフェイスでメソッドを呼び出し、FreeCellポリモーフィズムを使用して正しい doSomething() メソッドが呼び出されます。

(切り替えのように)使用する代わりにinstanceof、同じメソッドを呼び出すことができますが、各クラスはローカルオーバーライドに従ってそれを実装します。これは、多くのフレームワークで非常に強力で一般的な機能です。

代わりに書く:

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

次のように入力できます。

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

}

ファクトリ パターンは、いくつかの製品サブクラスの 1 つを返します。ファクトリ パターンを使用する必要があります。スーパー クラスと多数のサブクラスがあり、提供されたデータに基づいて、サブクラスの 1 つのオブジェクトを返す必要があります。

ここに画像の説明を入力

リンク:

抽象工場パターン

工場パターン

于 2012-11-16T22:01:22.527 に答える
0

2 つのセットを持っていて、セルが取得されたときにセルを一方から他方に移動できますか? たとえば、最初はセルがfreeSetいっぱいでtakenSet空のセルがあります。セルが取得されると、それらは freeSet から takenSet に移動されます。TakenCell と FreeCell の上にインターフェイスがある場合は、同じインターフェイスで各セットを入力できます。

あるいは...

FreeCellandの定義を確認することは役に立ちますがTakenCell、Taken であることを示すために入力される null 許容フィールドを持つ同じオブジェクトとしてそれらをモデル化できると思います。その後、2 つのセット タイプを同じクラスに使用できます。

于 2012-11-16T22:00:21.017 に答える
0

Visitor Patternを使用できます。これらの FreeCell と TakenCell は実装する必要があります

訪問可能なインターフェース

interface Visitable {
    void accept(Visitor visitor);
}

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

Visitor の visit(FreeCell freecCell) メソッドの実装では、次のようになります。

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

Visitor の visit(TakenCell takenCell) メソッドの実装では何もありません

両方のクラス: FreeCell と TakenCell のメソッド accept(Visitor visitor) には、次のものが必要です。

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

forループでは、次のものが必要です。

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

someConcreteVisitor は Visitor の実装者のインスタンスです。

この for ループがあるクラスも Visitable にすることができます。

于 2014-07-24T20:35:03.607 に答える
0

あなたのコードは見苦しくなく、読みやすく、アプリケーション ロジックを明確に表現しています。

通常、単一のinstanceofテストは心配する必要はありません。その使用は一般的です。「マーカー インターフェース」は によってテストされinstanceofます。あなたの例は一種のマーカーインターフェースです。

そしてinstanceof非常に高速で、明らかに JVM はそれを最適化する価値があると考えています。

ただし、一連のinstanceofテストは問題の兆候である可能性があります。列挙が完全であることを確認するためにガードを追加します。

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); 
于 2012-11-16T22:18:46.107 に答える