6

私は最近 Java アプリケーションを開発しており、GoF の状態パターンに従ってコードを整理しようとしています。

このプログラムは、マルチエージェント システムのエージェントを使用して、「スーパー エージェント」に代わって命令を評価します (以下の例)。

スーパー エージェントは 2 つの状態で存在することができ、状態をチェックしてから状態固有の動作を実行する if ステートメントを至る所に配置するのは面倒です。

これは、プログラムの (非常に) 単純化されたバージョンです。実際の実装には、より多くの状態固有の動作があります。

public class superAgent
{
    //the state of the super agent
    private States state;

    //Contains information related to the operation of exampleClass. This should not be exposed through mutator methods.
    private HashMap<String, SpecificInstructionData> instructionData

    private LinkedBlockingQueue<ExampleInstruction> exampleQueue

    private final Object instructionLock = new instructionLock

    public enum States
    {
        STATE1,
        STATE2;
    }

    public void setState(state s)
    {
        state = s
    }

    //Called by a thread that continuously takes from the queue 
    private void runningThread()
    {
        while(isRunning)
        {
            synchronized(instructionLock)
            {
                ExampleInstruction ei = exampleQueue.take();
                //Add some data about the instruction into instructionData
                //send the instruction to an available agent
            }
        }
    }

    public void instructionResponseRecievedFromAgent()
    {
        if(state == States.STATE1)
        {
            doState1Behavior();
        }
        else if(state == States.STATE2)
        {
            doState2Behavior();
        }
    }

    private void doState1Behavior()
    {
        synchronized(instructionLock)
        {
            //make state specific modifications to instructionData
        }
    }

    private void doState2Behavior()
    {
        synchronized(instructionLock)
        {
            //make state specific modifications to instructionData
        }
    }
}

状態パターンは、特定の状態の動作を GoF パターンに従ってさまざまなクラスにカプセル化するのに最適です (superAgent クラスがコンテキストになります)。ただし、2 つの問題があり、どちらも (IMO) カプセル化を破ります。

  1. ほとんどの状態固有の動作では、スーパー エージェントのプライベート メンバー (上記の例では、instructionData) を変更する必要があります。メンバーには、おそらくアクセスできないはずのデータが含まれており、ラッピング クラスに対して変更可能であってはなりません

  2. 状態固有の動作は、状態固有ではない動作と同期する必要があります。ロック オブジェクト (上記の例の instructionLock) を公開するかゲッターを使用して公開しないと、状態とコンテキストはロックを共有できません。ロックを公開すると、ラップ/拡張クラスで使用される可能性があるため、OOP に違反します。

上記の例と2つの点を念頭に置いて、この状態固有の動作をカプセル化する方法について誰か提案がありますか?

4

1 に答える 1

2

状態インスタンスとインスタンスの間でDouble DispatchsuperAgentを使用してカプセル化を壊さないようにすることで、両方の問題を解決できます。

State パターンを既に実装しているとします。instructionResponseRecievedFromAgent次のようになります。

public void instructionResponseRecievedFromAgent() {
  state.instructionResponseRecievedFromAgent();
}

それぞれがこのように二重ディスパッチを使用して State実装します。instructionResponseRecievedFromAgent

abstract class State {
  abstract void instructionResponseRecievedFromAgent();
}

class State1 extends State {
  void instructionResponseRecievedFromAgent() {
    // instance variable
    agent.instructionResponseRecievedFromAgentFromState1();
  }
}

class State1 extends State {
  void instructionResponseRecievedFromAgent() {
    // instance variable
    agent.instructionResponseRecievedFromAgentFromState2();
  }
}

これを行うと、それぞれにStateをするかを指定させますが、それを行う方法を決定するのはインスタンスです。公開することなく、状態とロックに完全にアクセスできます。superAgent

最後に、 と in の両方を実装instructionResponseRecievedFromAgentFromState1instructionResponseRecievedFromAgentFromState2ますsuperAgent

public void instructionResponseRecievedFromAgentFromState1() {
    //this is doState1Behavior original implementation
    synchronized(instructionLock)
    {
        //make state specific modifications to instructionData
    }
}

public void instructionResponseRecievedFromAgentFromState2() {
    //this is doState1Behavior original implementation
    synchronized(instructionLock)
    {
        //make state specific modifications to instructionData
    }
}

eventinstructionResponseRecievedFromAgentFromState1instructionResponseRecievedFromAgentFromState2は public メソッドであり (Stateインスタンスがそれらを呼び出すことができるため)、State パターンのコンテキストでのみ使用されることを意図していることに注意してください。

于 2013-11-04T17:46:24.210 に答える