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


9

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

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

  • Upgrade1自動車自体に適用される各状態の価格に追加されます。
  • ShippingShippingリストに少なくとも1つの状態がある場合、inStockに設定されfalseます。

たとえば、始まるprice = 1inStock = 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削除され、何かが別の状態の設定があるかどうかを確認しなければならinStockfalseには、の除去はのでShippingちょうどすることはできませんinStock = true。呼び出しごとにUpgrade増加priceします。次に、デフォルト値の定数を追加し、それらに基づいて再計算を試みました。

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

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

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


1
Stateを独自のクラスに抽象化するポイントは、相互に作用しないロジックを分離して、コードを簡略化することです。ただし、ビジネスロジックは、ロジックがリンクされていることを示しているため、リンクに戻る必要があり、その結果、この神はひどい混乱を招きます。ここで問題となっているのはオブザーバーパターンではなく、Stateで適用しているRoleパターンです。
ArTs

手作業で行っている場合、どのように問題を解決しますか?
James Youngman

@JamesYoungman私は3番目のオプション-外部マネージャーを使用してしまいました。この場合の紙に書く規則は単純ですが、この場合、言語がそれらを実装するために提供するオプションは制限さます。したがって、設計パターンの必要性。「手動でどのように行うのか」について考えることは、明確なルールのセットを適用するよりも、アルゴリズムに対してより効果的です。
user1803551

@ user1803551よく選んだ。
TulainsCórdova16年

すべてのイベントに対して1つのイベントハンドラーを用意します。このハンドラは、オブジェクトの完全な状態を再計算するための単なるエントリポイントです。それは典型的な問題です。「上から下、左から右」のフォームを操作すると、マニフェストが表示されます。すべてがA-OKですが、途中で何かを変更しても、正しく再計算されません。「イベント処理の順序をどのように保証できますか?」と尋ねるようになった場合、これで何をすべきかがわかります。
レーダーボブ2017年

回答:


1

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

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


0

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

これが私がコードを変更した方法です。独自の実装を行っているため、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;
}

こちらがマネージャーです。それは追いついていCar秒その管理(観測可能な)仕事Stateの(オブザーバー)。

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メソッドへの不要な呼び出しを排除できます。
  • オブザーバーは、さまざまな場面でより「更新」に似たメソッドを公開したい場合があります。ただ、一例として、彼らは現在の分割可能updateにする方法をupdateOnAddし、updateOnRemove彼らは唯一のこれらの変化のいずれかに興味がある場合。その後、addStateおよびremoveStateメソッドはそれに応じて更新されます。前のポイントに加えて、このアプローチは、堅牢で拡張可能で柔軟なメカニズムになる可能性があります。
  • States を追加および削除する指示を与えるもの、およびそれがいつ発生するかは、質問には重要ではないため、指定しませんでした。ただし、この回答の場合、次の点を考慮する必要があります。マネージャーのメソッドを呼び出す前に(空のコンストラクターが公開Stateされていない)で作成する必要があるためCaraddStateand removeStateメソッドはaを取得する必要がなくCar、から読み取ることができstate.carます。
  • デフォルトでは、オブザーバーへの登録順にオブザーバーに通知されます。別の順序を指定できます。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.