設計モードシリーズの4:観察者モード


前言
観察者モードは設計モードに属する行動型モードであり、 とは、方法や状態など、対象の動作が変化することを意味する.では、観察者モードはどんなモードですか?すなわち,観察者モードが解決する一対の多依存関係は,一つのオブジェクトの状態が変化すると,他のこのオブジェクトに依存するオブジェクトが通知され,それに応じて変化する.しかし、定義的には理解しにくい.簡単な例から観察者パターンをより深く理解することができる.
問題の背景
ある会社の社員2人は、主管が事務室を出た後、一人は株を見て、一人はゲームをしていた.会社にはフロントがあり、この2人の職員はフロントに主管が帰ってきたときに主管に他のことをしているのを見せないように知らせた.
コード実践1
上記のシーンに基づいて、次は私が書いた最初のコードです.
//     
public abstract class Employee{
    protected String name;
    //     
    protected Notifyer notifyer;
    public Employee(){}
    public Employee(String name,Notifyer notifyer){
        this.name = name;this.notifyer = notifyer;
    }

    public void setName(String name){this.name = name;}
    public String getName(){return name;}

    //         ,                        
    public abstract void update();
}

//      
public class StockEmployee extends Employee{
    public StockEmployee(String name,Notifyer notifyer){super(name,notifyer);}

    public void update(){
        System.out.println("    " + notifyer.getName() + " " + notifyer.getMessage() + "   ," + getName() + "     ,    !");
    }
}

//      ,        
public class GameEmployee extends Employee{
    public GameEmployee(String name,Notifyer notifyer){super(name,notifyer);}

    public void update(){
        System.out.println("    " + notifyer.getName() + " " + notifyer.getMessage() + "   ," + getName() + "     ,    !");
    }
}

//                              
public abstract class Notifyer{
    //       ,       ,         
    protected String name;
    protected String message;
    public Notifyer(String name){this.name = name;}
    //       
    protected void setMessage(String message){this.message = message;}
    protected String getMessage(){return message;}

    public String getName(){return name;}
    public void SetName(String name){this.name = name;}
    public abstract void notifyObserver();
}

public class Receptionist extends Notifyer{
    private List observers = new ArrayList();

    public Receptionist(String name){super(name);}
    //       ,          
    public void addObserver(Employee emp){observers.add(emp);}
    //       ,                  ,       
    public void removeObserver(Employee emp){observers.remove(emp);}

    public void notifyObserver(){
        for(Employee emp : observers){
            emp.update();
        }
    }
}

//    
public class Test{
    public static void main(String[] args){
        //      
        Receptionist rep = om.new Receptionist("  ");
        rep.setMessage("     ");

        Employee stockEmp = om.new StockEmployee("  ",rep);
        Employee gameEmp = om.new GameEmployee("  ",rep);
        Employee gameEmp2 = om.new GameEmployee("  ",rep);

        rep.addObserver(stockEmp);
        rep.addObserver(gameEmp);

        rep.notifyObserver();
    }
}

最後のプログラムの実行結果は次のとおりです.
私がプログラムで使用しているのは抽象クラスで、抽象クラスはクラスに対する抽象で、クラスはオブジェクトに対する抽象です.しかし,通知者が全く関連のないオブジェクトである可能性があることを考慮すると,抽象クラスを使用するのは合理的ではないので,インタフェースに変えることができ,インタフェースはクラスの行為に対する抽象であり,通知者の核心的な職責は観察者に具体的な通知を伝えることであり,その他は最も重要ではない.コードをさらに最適化します.
コード実践2
//                
public interface INotify{
    void addObserver(Employee emp);
    void removeObserver(Employee emp);
    void notifyObserver();
    String getName();
    String getMessage();
    void setMessage(String message);
    void setName(String name);
}

//       
public class Receptionist implements INotify{
    private List observers = new ArrayList();
    private String message;
    private String name;

    public Receptionist(String name,String message){
        this.name = name;
        this.message = message;
    }
    //       ,          
    public void addObserver(Employee emp){observers.add(emp);}
    //       ,                  ,       
    public void removeObserver(Employee emp){observers.remove(emp);}

    public String getMessage(){return message;}
    public void setMessage(String message){this.message = message;}
    public String getName(){return name;}
    public void setName(String name){this.name = name;}

    public void notifyObserver(){
        for(Employee emp : observers){
            emp.update();
        }
    }
}

//         ,    (     10     ,                       )
public class AlarmClock implements INotify{
    private List observers = new ArrayList();
    private String message;

    private String name;

    public AlarmClock(String name,String message){
        this.name = name;
        this.message = message;
    }

    //       ,          
    public void addObserver(Employee emp){observers.add(emp);}
    //       ,                  ,       
    public void removeObserver(Employee emp){observers.remove(emp);}

    public String getMessage(){return message;}
    public void setMessage(String message){this.message = message;}
    public String getName(){return name;}
    public void setName(String name){this.name = name;}

    public void notifyObserver(){
        for(Employee emp : observers){
            emp.update();
        }
    }
}

//   Employee       Notifyer     INotify  ,         

//       
public class Test{
    public static void main(String[] args){
        //      
        AlarmClock alarmClock = om.new AlarmClock("    ", "    ");
        Employee stockEmp = om.new StockEmployee("  ",alarmClock);
        Employee gameEmp = om.new GameEmployee("  ",alarmClock);
        Employee gameEmp2 = om.new GameEmployee("  ",alarmClock);

        alarmClock.addObserver(stockEmp);
        alarmClock.addObserver(gameEmp);

        alarmClock.notifyObserver();
    }
}

最後のテスト結果は次のとおりです.
以上の2つのテスト結果から,Notifyerのmessage属性が変化すれば,その所管する観察者の動作も更新されることが分かった.テストコードに3番目のオブジェクトが追加されていないことに注意してください.これにより、Notifyerの状態が変化しても動作が更新されません.これにより、前の2つのオブジェクトだけが動作の更新が発生します.つまり、Notifyerというオブジェクトの状態が変化することで、他の2つのオブジェクトが変化します.観察者モードもこの場合に用いられる.1つのオブジェクトの状態が変化して他のオブジェクトの状態も変化し、どれだけ具体的なオブジェクトの状態が変化するか分からない場合は、観察者モードを使用します.オブザーバーモードには、上のコードのNotifyerオブジェクトとINotifyオブジェクトに対応する2つのキーオブジェクトSubjectとObserverがあります.(上のEmployeeオブジェクトに対応します).Subjectの状態の変化は、Objectオブジェクトの状態の変化を引き起こす可能性があります.Subjectオブジェクトの状態の変化は任意の数の観察者オブジェクトに通知することができます.Subjectは、特定の観察者を知る必要はありません.観察者も、他の観察者が存在するかどうかを知る必要はありません.実際には、観察者モードは主にデカップリングのためです(実際には23種類の設計モードのいずれもこれを体現しているが、異なる設計モードの体現程度が異なるにすぎない)ため、オブジェクト間では具体的に依存せず抽象に依存する.このような設計の利点は、具体的な大きな修正に対して他の抽象に依存するクラスに影響を与えず、クラス間の結合度を低下させる.
上記のコードでは,具体的なSubjectはObserverオブジェクトに依存しているが,このような依存は影響を及ぼすのではないかと考えられる.答えは肯定的だ.
SubjectもObserverも抽象的であるが,このような抽象的なObserverがシステム中にずっと存在することは保証されないため,このような依存はプログラム間の結合を最大限に減らしたが,この場合には問題がある.では、この結合を解決する他の方法はありませんか?あります.委託されたイベントメカニズムを用いてさらなるデカップリングを達成することができる.イベント依頼は依頼者のメソッドをイベントで実行することができ、イベント依頼はメソッドを実行するオブジェクト、メソッド、パラメータリストだけが必要である.これにより、イベント依頼対象はこの3つのパラメータに基づいて作成対象の指定方法を実行することができ、直感的に理解することができ、エージェントに似ているが、エージェントとは異なる.Javaでは既存のイベント委任モデルが使用できないため,独自のイベント委任プロセッサを実現できる.実装後,Subjectにこのイベント委任プロセッサを導入し,対応するイベント応答方法を追加したが,Observerオブジェクトは何も変更する必要はなかった.具体的なイベント依頼プロセッサのコードは、私のGithubのソースコードを参照することができます.
最後に、観察者のパターンを簡単にまとめます.
  • オブザーバモードは、主に1つのオブジェクトの変化が他のオブジェクトの状態の変化をもたらす場合に適用される
  • .
  • 抽象低減を用いる関連オブジェクトの結合を必要とし、具体的には依存せず抽象
  • に依存する.
  • 観察者モードの不足は、Observerオブジェクトが存在しない場合にシステムに重大な影響を及ぼすことにある
  • .