web-dev-qa-db-ja.com

ストラテジーパターンのコンポジターにコンポジションを含めることは許可されていますか?

私の動物園には、カメ、鳥などのさまざまな動物がいます。それらはすべて、水泳、飛行などの共通の特性を共有しているため、戦略パターンはそれらをモデル化するのに適していると思いました。でも、コンポジターからコンポジションのメソッドを呼び出したいのですが。このMWEを参照してください。

Animal.Java(抽象クラ​​ス、構成)

public abstract class Animal {
    Movement movement;
    int metersSwam = 0;

    void swimMeters(int meters){
        metersSwam += meters;
    }

    void swim() {
        movement.swim();
    }

    void fly() {
        movement.fly();
    }
}

Turtle.Java(動物を拡張する)

public class Turtle extends Animal {
    public Turtle() {
        movement = new TurtleMovement(this);
    }
}

Movement.Java(インターフェース、コンポジター)

public interface Movement {
    void swim();
    void fly();
}

TurtleMovement.Java(ここに、Turtleのメソッドを呼び出す問題があります)

public class TurtleMovement implements Movement {
    Animal turtle;

    public TurtleMovement(Animal turtle) {
        this.turtle = turtle;
    }

    @Override
    public void swim() {
        turtle.swimMeters(10); //<--- here
        System.out.println("I can swim, just swam 10 meters");
    }

    @Override
    public void fly() {
        System.out.println("I can't fly");
    }
}

main.Java

public class Zoo {

    public static void main(String[] args){
        Animal animal = new Turtle();
        animal.fly();
        animal.swim();
    }
}

だから私の質問は基本的に、TurtleMovementでTurtleのメソッドを呼び出すことは許可されていますか?そうでない場合、それを回避する方法はありますか、それとも戦略パターンが私の状況に理想的ではないのでしょうか?

2
Tyler D

モデル化している実際のドメインの観点から考えると役立ちます。

  • すべての動物が飛んだり泳いだりできるわけではありません。したがって、flyswimなどのメソッドは、実際にはクラスAnimalに適合しません。指向的な意味で、どのクラスのメソッドを呼び出しても、「ランダムに」エラーが発生するようには見えません。クラスAnimalを呼び出す場合は、少なくともまとめてすべての動物が共有するメソッドをいくつか作成します。忘れないでください最小の驚き
  • ムーブメントが水泳と飛行を同時に行うことはできません。 1つの動きは実際にはone運動のスタイルを表しています。問題は、MovementをTurtleMovementなどに拡張しようとすることに起因します。各動物独自の動きはありません。すべての動物シェアさまざまな動き。 実際の種類の動きの後の動きをモデル化します。動物の後ではありません。
  • new SomeClass(this)に注意してください。このタイプの抽象化から得られる唯一の力は、コードを別の場所に移動するか、周囲のコンテキストをキャプチャすることです(矢印など)。

つまり、every動物の動きを作成するのはなぜですか?その後、あなたは同じくらい多くの動きをするでしょう。

考慮してください:

_abstract class Animal
{
    Movement movement;

    void move()
    {
        movement.move();
    }
}

abstract class Movement
{
    void move();
}

class FlyMovement extends Movement
{
    @Override
    void move()
    {
        //fly somehow
        System.out.println("I fly");
    }
}

class SwimMovement extends Movement
{
    @Override
    void move()
    {
        System.out.println("I swim.");
    }
}

class Turtle : Animal
{
    public Turtle()
    {
        movement = new SwimMovement();
    }
}
_

これは間違いなくあなたが思いつくことができる最高のものではありませんが、それはあなたにアイデアを与えるかもしれません。ムーブメント内のAnimal情報が必要な場合は、複数のディスパッチを検討しています。また、Animalの具体化をまったく作成しないことも検討してください。カスタムムーブメントによって完全に特徴付けられる、単なる非抽象Animal

_public class Animal
{
    public Animal(List<Movement> movementCapabilities)
    {
        //keep and use as necessary.
    }
}
_

ただいくつかのアイデア...

[〜#〜]編集[〜#〜]


フィリップのコメント は非常に良い命題です。一般に、Animalはビヘイビアーの単純な「コンビネーター」と考えてください。クラスに存在する必要がある具体的な詳細はほとんどありません。詳細はおそらく別の動作に関連します。動物が動くと、Movementができます。実際、交差した距離は、動物よりも動きの行動に密接に関連しています。動物が食べる、あなたはFeedingを持っています。数量なども、FeedingではなくAnimalの動作に密接に関連しています。

したがって、あなたはdisjoint(実際には直交)の振る舞いをし、最終的には次のようにします。

Animal wolf = new Animal(new FourLeggedRunningMovement(...), new CarnivoreDiet(...), new CrepuscularActivityPattern(...), ...);

2つの楽章が必要な場合は、次のように、新しいクラスを使用して2つを1つにまとめることができます。

_FourLeggedRunningMovement fourLegRun = new FourLeggedRunningMovement(...);

FourLeggedWalkingMovement fourLegWalk = new FourLeggedWalkingMovement(...);

List<Movement> movements = new List<Movement>()
{
    fourLegRun,
    fourLegWalk
}

CompositeMovement fourLegWalkRun = new CompositeMovement(movements);

//Now you can model two (or more) movements in one go.
_

重要な持ち帰りメッセージは次のとおりです。-階層を設定してクラスを編成する前に、かなりの時間(かなり比例して)を費やして考えてくださいおそらく自分がそれらをどのように使用しているのかを理解してください

もちろん、ここの数行のコードでは、非常に多くの詳細が省略されていますが、非常に「概念的に密集」しているため、細かいところまでさかのぼることができます。オブジェクト指向プログラミングでは考える前に行う(!)

4
Vector Zita