web-dev-qa-db-ja.com

オブザーバーパターンは、オブザーバーが互いに独立していない場合に適していますか?

2つのプロパティがあるclass Carがあります:int priceboolean inStock。また、List of abstract class State(空のクラス)も保持します。車に適用できる2つの状態があり、それぞれが独自のクラスで表されます:class Upgrade extends Stateおよびclass Shipping extends State

Carは、2つの状態をそれぞれいくつでも保持できます。州には次の規則があります。

  • Upgrade:自動車の後に適用される各州の価格に1を追加します。
  • Shipping:リストに少なくとも1つのShipping状態がある場合、inStockfalseに設定されます。

たとえば、price = 1およびinStock = trueで始まります。

add Shipping s1    --> price: 1, inStock: false
add Upgrade g1     --> price: 1, inStock: false
add Shipping s2    --> price: 2, inStock: false
add Shipping s3    --> price: 3, inStock: false
remove Shipping s2 --> price: 2, inStock: false
remove Upgrade g1  --> price: 1, inStock: false
remove Shipping s1 --> price: 1, inStock: false
remove Shipping s3 --> price: 1, inStock: true

それぞれの追加操作と削除操作がオブザーバーに通知するオブザーバーパターンについて考えていました。私はこのようなことを念頭に置いていましたが、私が提示したルールに従っていません。

abstract class State implements Observer {

    public abstract void update();
}

class Car extends Observable {

    List<State> states = new ArrayList<>();
    int price = 100;
    boolean inStock = true;

    void addState(State state) {

        if (states.add(state)) {
            addObserver(state);
            setChanged();
            notifyObservers();
        }
    }

    void removeState(State state) {

        if (states.remove(state)) {
            deleteObserver(state);
            setChanged();
            notifyObservers();
        }
    }
}

class Upgrade extends State {

    @Override
    public void update(Observable o, Object arg) {

        Car c = (Car) o;
        int bonus = c.states.size() - c.states.indexOf(this) - 1;
        c.price += bonus;
        System.out.println(c.inStock + " " + c.price);
    }
}

class Shipping extends State {

    @Override
    public void update(Observable o, Object arg) {

        Car c = (Car) o;
        c.inStock = false;
        System.out.println(c.inStock + " " + c.price);
    }
}

明らかに、これは機能しません。 Shippingが削除されると、somethingは、inStockをfalseに設定する別の状態があるかどうかを確認する必要があるため、 Shippingの削除はinStock = trueだけではありません。 Upgradeは、呼び出しごとにpriceを増やします。次に、デフォルト値の定数を追加し、それらに基づいて再計算を試みました。

私は決してパターンを課そうとしているのではなく、上記の要件の解決策を見つけようとしているだけです。実際にはCarには多くのプロパティが含まれ、この方法で適用できる多くの状態があることに注意してください。私はこれを行ういくつかの方法を考えました:

  1. 各オブザーバーはCarを受け取るため、現在登録されている他のすべてのオブザーバーを確認し、それに基づいて変更を加えることができます。このようにオブザーバーを巻き込むことが賢明かどうかはわかりません。
  2. オブザーバーがCarに追加または削除されると、再計算されます。ただし、この再計算は、allオブザーバーで、追加または削除されたオブザーバーに関係なく実行する必要があります。
  3. Addメソッドとremoveメソッドを呼び出して再計算を行う外部「マネージャー」クラスを用意します。

説明されている動作を実装するための適切な設計パターンは何ですか?どのように機能しますか?

9
user1803551

私はオプション3を使用してしまいました-外部マネージャーを使用します。マネージャーは、StatesからCarsを追加および削除し、これらの変更が発生したときにオブザーバーに通知する責任があります。

これが私がコードを変更した方法です。独自の実装を行っているため、JDKのObservable/Observerを削除しました。

Stateは、そのCarへの参照を保持します。

abstract class State {

    Car car;

    State(Card car) { this.car = car; }

    public abstract void update();
}

class Upgrade extends State {

    @Override
    public void update() {

        int bonus = car.states.size() - car.states.indexOf(this) - 1;
        car.price += bonus;
        System.out.println(car.inStock + " " + car.price);
    }
}

class Shipping extends State {

    @Override
    public void update() {

        car.inStock = false;
        System.out.println(car.inStock + " " + car.price);
    }
}

Carはその状態のみを保持し(混乱を避けるため:プロパティ)、Statesの追加と削除は処理しません。

class Car extends Observable {

    List<State> states = new ArrayList<>();
    int price = 100;
    boolean inStock = true;
}

こちらがマネージャーです。 Cars(オブザーバー)を管理するStates(監視可能)ジョブを追い越しました。

class StatesManager {

    public void addState(Card car, State state) {

        car.states.add(state);
        for (State state : car. states)
            state.update;
    }

    public void removeState(Card car, State state) {

        car.states.remove(state);
        for (State state : car. states)
            state.update;
    }
}

覚えておくべきいくつかのこと:

  • すべてのオブザーバーはすべての変更について通知されます。より巧妙なイベント分散スキームは、オブザーバーのupdateメソッドへの不要な呼び出しを排除できます。
  • オブザーバーは、さまざまな場面でより「更新」のようなメソッドを公開したい場合があります。例として、これらの変更の1つだけに関心がある場合、現在のupdateメソッドをupdateOnAddupdateOnRemoveに分割できます。次に、addStateおよびremoveStateメソッドがそれに応じて更新されます。前のポイントに加えて、このアプローチは、堅牢で拡張可能で柔軟なメカニズムになる可能性があります。
  • Statesを追加および削除する指示を与えるもの、およびそれがいつ発生するかは、質問には重要ではないため、指定しませんでした。ただし、この回答の場合、次の点を考慮する必要があります。 Stateは、マネージャーのメソッドを呼び出す前にCar(空のコンストラクターが公開されていない)で作成する必要があるため、addStateおよびremoveStateメソッドは必要ありません。 Carを取得し、state.carから読み取ることができます。
  • デフォルトでは、オブザーバーへの登録順にオブザーバーに通知されます。別の順序を指定できます。
0
user1803551

異なる方法でシステムを因数分解すると、オブザーバーはうまく機能します。状態自体をオブザーバーにする代わりに、2つの新しいクラスを「状態変更オブザーバー」にすることができます。1つのオブザーバーが「price」を更新し、別のオブザーバーが「inStock」を更新します。このように、inStockに依存する価格のルールがない場合、またはその逆の場合、つまり状態の変化を見ることだけですべてを計算できる場合、それらは独立しています。この手法は「イベントソーシング」と呼ばれます(たとえば、- https://ookami86.github.io/event-sourcing-in-practice/ を参照してください)。注目すべきアプリケーションがいくつかあるのは、プログラミングのパターンです。

より一般的な質問に答えると、オブザーバー間に依存関係がある場合があります。たとえば、ある観察者が別の観察者の前に反応するようにしたい場合があります。このような場合、通常、Observableクラスのカスタム実装を作成して、順序や依存関係を処理することができます。

1
battlmonstr