オブザーバーパターン実力分析

一般的に、オブジェクト指向分析と設計には、通常の関数呼び出し以外に、コールバック関数がよく用いられる三種類のイベント処理メカニズムが存在します。また、J2EEではリスナーベースのイベント処理メカニズムも提供されています。資料を調べて、ActionおよびActionListenerのメカニズムを分析し、分析例を完成させてください。

まず、オブザーバーパターンを理解します。

オブザーバーパターン (Observer Pattern)

現実世界におけるオブザーバー

オブザーバーパターンは、Publish-Subscribe(発行-購読)パターンとも呼ばれます。例えば、RSS(WeChatプッシュの元祖)の購読が挙げられます。あるブログ(被観察者)の投稿者が記事を更新すると、購読者のリーダー(観察者)は自動的に更新を受け取ることができます。この発行-購読の仕組みを、私たちはオブザーバーパターンと呼びます。

オブザーバーパターン

Gang Of Four(GoF)によるオブザーバーパターンの記述:

  • 意図

    オブジェクト間の一対多の依存関係を定義します。あるオブジェクトの状態が変化したとき、それに依存するすべてのオブジェクトに通知が届き、自動的に更新されます。

  • 別名

    依存者(Dependents)、発行-購読(Publish-Subscribe)

  • 動機

    システムを一連の相互に協力するクラスに分割すると、関連するオブジェクト間の一貫性を維持する必要が生じるという一般的な副作用があります。一貫性を保つためにクラス間を密結合にすることは望ましくありません。なぜなら、それはそれらの再利用性を低下させるからです。

…

  • 参加者

    • Subject(サブジェクト、目標)

      サブジェクトは自身のオブザーバーを知っています。任意の数のオブザーバーが同じサブジェクトを観察できます。

      オブザーバーオブジェクトを登録および削除するためのインターフェースを提供します。

    • Observer(オブザーバー、観察者)

      サブジェクトが変化したときに通知を受け取る必要があるオブジェクトに対して、更新インターフェースを定義します。

    • ConcreteSubject(具象サブジェクト、具体目標)

      関連する状態を各ConcreteObserverオブジェクトに保存します。

      自身の状態が変化したとき、自身の各オブザーバーに通知します。

    • ConcreteObserver(具象オブザーバー、具体観察者)

      ConcreteSubjectオブジェクトへの参照を保持します。

      関連する状態を保存します。これらの状態はサブジェクトの状態と一致している必要があります。

      Observerの更新インターフェースを実装し、自身の状態をサブジェクトの状態と一致させます。

オブザーバーパターンは、以下のような場合によく適用されることがわかります:

  • 二つのオブジェクトが依存関係にある場合。
  • あるオブジェクトを変更する際に、他のオブジェクトも同時に変更する必要がある場合。

通常の関数呼び出しやコールバック関数と比較して、オブザーバーパターンの使用は、低コストでオブジェクト間の一貫性を維持することができ、オブジェクト間の結合度を大幅に低下させ、同時にブロードキャスト効果を達成できます。これは前者の二つでは実現できないことです。オブザーバーパターンの使用は、コードの再利用性を大幅に向上させます。

そして、J2EEにおけるActionとActionListenerは、まさにオブザーバーパターンの生きた例です。

J2EEにおけるAction&ActionListener

Action&ActionListenerは、MVCパラダイムにおいてモデルとビューの結合度を下げるためにうまく適用できます。

良い例を見つけました。コードは以下の通りです:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

public class Frame extends JFrame {

  ActionListener observer1 = new Observer("First Observer");
  ActionListener observer2 = new Observer("Second Observer");
  ActionListener observer3 = new Observer("Third Observer");

  public Frame(){
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    init();
  }

  private void init(){
    addChangingTextField();
    pack();
    setVisible(true);
  }

  private void addChangingTextField(){
    //Observable class TextField. each time
    //An action or event occurs the observers will be updated
    //Almost all the Swing classes has this kind of behavior
    JTextField dataTextField = new JTextField();

    // Observer registering to the observable class.
    dataTextField.addActionListener(observer1);
    dataTextField.addActionListener(observer2);
    dataTextField.addActionListener(observer3);

    add(dataTextField);
  }

  public static void main(String [] args){
  	Frame frame = new Frame();
  }
}

class Observer implements ActionListener{

  private String name;
  private String message;

  public Observer (String s){
  	name = s;
  }

  public void actionPerformed(ActionEvent e) {
    JTextField textField = (JTextField) e.getSource();
    message = textField.getText();
    JOptionPane.showMessageDialog(null,
    name + " has been confirmed about the change, Thanks!! "
    + " The Message was: " + message);
  }
}

分析

二つのクラスはそれぞれAction/ActionListenerの役割を担っています。frame.dataTextFieldはaddChangingTextFieldメソッド内で三つのリスナー(observer1, observer2, observer3)にバインドされています。フレーム内のテキストフィールドが編集されると(状態が変化)、ObserverのactionPerformedメソッドがトリガーされ、対応するテキストが表示されます。

この例では、frame.dataTextFieldがConcreteSubjectとして、observer1, observer2, observer3がConcreteObserverとして機能しています。そして、それらが実装しているAction/ActionListenerメソッドを定義したインターフェースが、それぞれのSubjectとObserverに対応します。

オブザーバーをバインドした後、サブジェクトはオブザーバーの具体的な動作を気にせず、自身の状態が変化したときにオブザーバーに通知するだけです。何らかのアクションを実行するかどうかは、オブザーバー自身が決定します。したがって、この方法は非常に優れており、関数を明示的に呼び出したり、コールバック関数を実行したりしてアクションをトリガーする方法よりもはるかに優れており、大いに参考になります。

/デザインパターン/ /システム分析と設計/