2017-01-20 4 views
5

私は車に関する問題を説明しようとします。私はAbstractCarを持っており、私の図書館のユーザー(開発者)は多くのコンクリート車を作成します。このAbstractCarには状態があり、この状態はライブラリの正しい作業にとって非常に重要です!車だけがその状態を制御することができます(ドライバなどはありません)。メソッドの開始/停止は、メソッドの開始時と終了時に状態が変化します。また、すべての車はインターフェースカーを実装する必要があります。Java:状態と継承を扱うためのデザインパターン

public enum State{ 
    STARTING, STARTED, STOPPING, STOPPED 
} 

public interface Car{ 
    public void start(); 
    public void stop(); 
    public State getState(); 
} 

私は2つの変種を試しました。

バリアント図書館の利用者が状態を変更するために覚えておく必要がありますバリアント1で1

public abstract class AbstractCar implements Car{ 
    private State state; 
    public void setState(State state){...} 
    public State getState(){...} 
} 

public class ConcreteCar extends AbstractCar{ 
    @Override 
    public void start(){ 
    setState(stateK); 
    ... 
    setState(stateN); 
    } 

    @Override 
    public void stop(){ 
    setState(stateR); 
    ... 
    setState(stateO); 
    } 
} 

。彼がそれをやめたら、コードにバグがあります。それは彼のコントロールの外に既にあるので、ユーザは状態を忘れることはできませんが、私は多くの州や多くのメソッドを持っている場合、彼らはこれを変更することができるバリアント2では

バリアント2

public abstract class AbstractCar implements Car{ 
    private State state; 
    protected void doOnStart(){ } 
    protected void doOnStop(){ } 
    public final void start(){ 
    state=...; 
    doOnStart(); 
    state=...; 
    } 
    public final void stop(){ 
    state=...; 
    doOnStop(); 
    state=...; 
    } 
} 

public class ConcreteCar extends AbstractCar{ 
    @Override 
    protected void doOnStart(){ 
    .... 
    } 

    @Override 
    protected void doOnStop(){ 
    ... 
    } 
} 

非常に良い方法ではありません。

どのようなパターンや技術にこのような問題を解決する方法をアドバイスできますか?

+3

バリアント2は、テンプレートメソッドパターンです:https://en.wikipedia.org/wiki/Template_method_pattern –

+0

@ cricket_007私は同じように、状態パターンについて読んだことがあります私はそれが状態に応じて行動を変えることを可能にすることを正しく理解している。しかし、私は違う振る舞いは必要ありません。私は状態を制御する必要があります。 –

+0

あなたのpsudeocodeから、開始、開始、停止、停止状態のようですか? –

答えて

0

状態デザインパターンを使用します。 https://sourcemaking.com/design_patterns/state

AbstractCarをコンテキストとして保持し、それを使用して車の状態を管理します。

私は次のように例を挙げます。うまくいけば私はそれを正しく理解する。バリアント2では

public interface IState { 
    public void changeState(Car inContext); 

    public void doSomething(); 
} 

public interface Car { 
    enum CarState{ 
     start, 
     stop, 
     auto 
    } 

    public void setState(CarState state); 
} 

public class AbstractCar implements Car { 
    IState m_currentState; 

    IState startState = new StartState(); 
    IState stopState = new StopState(); 
    IState autoState = new AutoNavigateState(); 

    public AbstractCar() { 
     m_currentState = stopState; 
    } 

    public void start() { 
     setState(CarState.start); 
     m_currentState.doSomething(); 
     m_currentState.changeState(this); 
    } 

    public void stop() { 
     setState(CarState.stop); 
     m_currentState.doSomething(); 
     m_currentState.changeState(this); 
    } 

    public void autoNavigate() { 
     setState(CarState.auto); 
     m_currentState.doSomething(); 
     m_currentState.changeState(this); 
    } 

    public void setState(CarState state) { 
     if (state == CarState.start) { 
      m_currentState = startState; 
     } else if (state == CarState.stop) { 
      m_currentState = stopState; 
     } else { 
      m_currentState = autoState; 
     } 
    } 

} 

public class StartState implements IState { 

    @Override 
    public void changeState(Car car) { 
     car.setState(CarState.stop); 
    } 

    @Override 
    public void doSomething() { 
     // TODO Auto-generated method stub 

    } 

} 

public class StopState implements IState{ 

    @Override 
    public void changeState(Car car) { 
     car.setState(CarState.start); 
    } 

    @Override 
    public void doSomething() { 
     // TODO Auto-generated method stub 

    } 

} 

public class AutoNavigateState implements IState{ 

    @Override 
    public void changeState(Car inContext) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void doSomething() { 
     // TODO Auto-generated method stub 

    } 

} 
0

それはすでに

彼のコントロール外

であり、それが望ましいため、ユーザーは、状態を忘れることができませんか?

public abstract class AbstractCar implements Car{ 
    ... 
    public final void start(){ 
    state=...; 
    doOnStart(); 
    state=...; 
    } 
    ... 
} 

抽象クラスでは、具体的なクラスのために使用する状態を決定します。
状態の変更はstart()メソッドで変更されるコンテキストに依存するため、コンクリートクラスのための非常に柔軟な解決策ではありません。

あなたは許可の両方にAbstractCarで抽象メソッドを使用し、そのように自分の状態を定義する方法を選択するために具体的なクラスを強制する必要があります。この具象クラスを想定

public abstract class AbstractCar implements Car{ 
    ... 
    public abstract State getStateBeforeStart(); 
    public abstract State getStateAfterStart(); 
    ... 
    public final void start(){ 
    state = getStateBeforeStart(); 
    doOnStart(); 
    state = getStateAfterStart(); 
    } 
    ... 
} 

あなたはまた、Javadocを使用することができますクラスのAPIと具体的なクラスの責任を適切に文書化して、クラスの適切な使用を優先します。

1

車が特定の瞬間にどの状態になり、どの遷移が許可されるかを完全に制御したい場合は、2番目の方法は基本的なパターンです。

サブクラスのコードを呼び出す方法を変更することができます(抽象メソッドやその他のコールバックを呼び出すなど)。基本パターンは同じです - AbstractCarのコードにはロジックが含まれます「外部」コードが呼び出される可能性のある定義されたポイントを持つ状態および遷移を含む。これらは「フック」と呼ばれることもあります。

このようなアプローチの例は、JSF life-cycleです。要求が複雑なワークフローを通過し、特定の段階(たとえば、検証)でユーザー提供のコードが実行される可能性があります。 直接は要求の状態を設定します。

あなたのユーザ(サブクラスの作成者)が車の状態に影響を与えることを許可したい場合は、次の状態遷移に影響を与えるコールバックからの戻り値を受け入れるか、単に適切なエラー処理を実行して、いくつかの例:

public final void start(){ 
    state=STARTING; 
    try { 
     doOnStart(); 
     state=STARTED; 
    } catch (RuntimeException e) { 
     // handle error 
     state=STOPPED; 
    } 
} 
関連する問題