1。オブザーバーモードの定義:
オブザーバーモードは、サブスクリプションパブリッシュモードとも呼ばれます。このモードでは、ターゲットオブジェクトがそれに依存するすべてのオブザーバーオブジェクトを管理し、状態が変更されたときに通知を積極的に発行します。これは通常、各オブザーバーの方法を呼び出すことによって達成されます。このパターンは通常、イベント処理システムで使用されます。
1。オブザーバーパターンの一般的な構造
まず、オブザーバーパターンのクラス図の説明を見てみましょう。
オブザーバーモードの役割は次のとおりです。
件名(要約トピックインターフェイス):追加、削除、通知などを含む、テーマクラスのオブザーバーリストの一連の操作を定義します。
具体的な科目(特定のトピッククラス):
オブザーバー(要約オブザーバーインターフェイス):トピッククラスの更新ステータスの操作を受け入れるようにオブザーバーを定義します。
concreteobserver(特定のオブザーバークラス):オブザーバーインターフェイスによるトピッククラス通知を更新するなどのロジックを実装します。
このクラス図から、テーマクラスがオブザーバーインターフェイスを実装するクラスリストを維持していることがわかります。テーマクラスでは、このリストを使用して、オブザーバーの一連の追加、削除、変更を実行します。オブザーバークラスは、更新方法を積極的に呼び出して、テーマクラスのステータス更新情報を理解することもできます。
上記のクラス図には、基本的なオブザーバーパターンのアイデアのみが説明されており、多くの欠点があります。たとえば、オブザーバーとして、特定のトピックなどを積極的に購読することもできます。次の例では、特定のビジネスロジックを適用するためにいくつかの変更を加えます。
2。オブザーバーモードの例
オブザーバーとトピッククラスを構築します。このクラスでは、オブザーバーがトピックを積極的にサブスクライブまたはキャンセルできるようになります。テーマカテゴリは、テーママネージャーによって均一に管理されています。以下はクラス図です。
主題:
パブリックインターフェイス件名{//オブザーバーパブリックボイドレジスタ(オブザーバーオブザーバー)を登録します。 //オブザーバーのpublic voidを削除する(オブザーバーオブザーバー); //すべてのオブザーバーに通知public void notifyobservers(); // Topic Class Public String getMessage();} Concertesubject:public class mySubject emplences subject {private list <Observer> Observers;プライベートブールの変更。プライベート文字列メッセージ; //オブジェクトロック、オブザーバーリストを同期してプライベート最終オブジェクトMutex = new Object(); public mySubject(){observers = new arrayList <Omberver>();変更= false; } @Override public void Register(Observer Observer){if(Observer == null)新しいnullpointerexception(); // if(!observers.contains(Observer))Observers.Add(Observer)を繰り返さないことを証明します。 } @Override public void remove(Observer Observer){Observers.Remove(Observer); } @Override public void notifyobservers(){// temp list <observer> tempobservers = null;同期(mutex){if(!chandide)return; tempobservers = new ArrayList <>(this.observers); this.changed = false; } for(Observer obj:tempobservers){obj.update(); }} //テーマクラスは新しいメッセージを公開しますpublic public void makeChanged(string message){system.out.println( "件名は変更を加えます:" +メッセージ); this.message = message; this.changed = true; notifyobservers(); } @Override public String getMessage(){return this.message; }}Concertesubjectが更新すると、リスト内のすべてのオブザーバーが通知され、通知を受け取った後にロジックを実装するためにオブザーバー更新方法が呼び出されます。 notifyobserversの同期ブロックに注意してください。マルチスレッドの場合、トピッククラスで通知を公開する際に他のスレッドでオブザーバーリストの追加と削除を避けるために、同期ブロックで一時リストが使用され、現在のオブザーバーリストを取得します。
主題管理:テーマクラスマネージャー
パブリッククラスの件名管理{//レコード名 - マッププライベートマップ<文字列、件名> subjectlist = new hashmap <string、subject>(); public void addSubject(string name、subject subject){subjectlist.put(name、subject); } public void addSubject(subject subject){subjectlist.put(subject.getClass()。getName()、subject); } public subject getSubject(string subjectname){return subjectlist.get(subjectname); } public void remodivesubject(string name、subject subject){} public void requirvesubject(subject expaching){} // singleton private manmanagement(){} public static management getInstance(){return SubjectManagementInstance.instance; } private static class subjectmanagementinstance {static final subjectmanagement instance = new SubjectManagement(); }}トピックマネージャーの目的は、オブザーバーがトピックを購読するときにトピックのインスタンスオブジェクトを取得することです。
オブザーバー:
パブリックインターフェイスオブザーバー{public void update(); public void setSubject(主題);} concerteobserver:public class myobserverはオブザーバー{private件名; //集中科目から通知メッセージを取得@Override public void update(){string message = subject.getMessage(); system.out.println( "from subject" + subject.getClass()。getName() + "message:" + message); } @Override public void setSubject(件名){this.subject = subject; } // subcirbeいくつかの件名public void subscribe(string subjectname){subjectmanagement.getInstance()。getsubject(subjectname).register(this); } // cancel cancel public void cancelsubscribe(string subjectname){subjectmanagement.getinstance()。getsubject(subjectname).remove(this); }}テスト:私たちは科目のクラスとオブザーバーを作家や読者に抽象化します
Public Class Observertest {private static mySubject Writer; @beforeclass public static void setupbeforeclass()throws Exception {writer = new mySubject(); // linus subjectmanagement.getInstance()。addsubject( "linus"、writer)という名前のライターを追加します。 } @test public void test(){//いくつかの読者myobserver reader1 = new Myobserver(); myobserver reader2 = new Myobserver(); myobserver reader3 = new Myobserver(); reader1.setsubject(writer); reader2.setsubject(writer); reader3.setsubject(writer); reader1.subscribe( "linus"); reader2.subscribe( "linus"); reader3.subscribe( "linus"); writer.makechanged(「私は新しい変更があります」); reader1.update(); }}上記は、オブザーバーパターンの小さな例です。各トピッククラスは、対応するオブザーバーリストを維持する必要があることがわかります。ここでは、特定のトピックの抽象レベルに基づいてさらに抽象化し、この収集を抽象クラスに入れて、リストを共同で維持することができます。もちろん、特定の操作は実際のビジネスロジックに依存します。
2。サーブレットのリスナー
サーブレットのリスナーについて話しましょう。オブザーバーパターンの別の形式、つまりイベント駆動型モデルについて話しましょう。上記のオブザーバーパターンのテーマの役割と同様に、イベント主導のモデルには、イベントソース、特定のイベント、リスナー、特定のリスナーが含まれます。
サーブレットのリスナーは、典型的なイベント駆動型モデルです。
JDKには、統一されたリスナーインターフェイスや統一されたイベントソースなど、イベント駆動型クラスのセットがあります。ソースコードは次のとおりです。
/***すべてのイベントリスナーインターフェイスが拡張する必要があるタグ付けインターフェイス。 * @since jdk1.1 */public interface eventlistener {}これはフラグインターフェイスであり、JDKは、すべてのリスナーがこのインターフェイスを継承する必要があると規定しています。
Public Class EventObjectはjava.io.serializable {private static final long serialversionuid = 5516075349620653480l; /***イベントが最初に発生したオブジェクト。 */保護された過渡オブジェクトソース。 /***プロトタイプイベントを作成します。 * * @paramは、イベントが最初に発生したオブジェクトをソースします。 * @exception Sourceがnullの場合はIllegalargumentException。 */ public eventObject(object source){if(source == null)新しいIllegalargumentException( "null source"); this.source = source; } /***イベントが最初に発生したオブジェクト。 * * @イベントが最初に発生したオブジェクトをRETURNします。 */ public Object getSource(){return source; } /***このEventObjectの文字列表現を返します。 * * @returnこのeventObjectの文字列表現。 */ public String toString(){return getClass()。getName() + "[source =" + source + "]"; }}vEtObjectは、JDKによって指定された統一イベントソースです。 EvenObjectクラスは、イベントソースとイベントソースを取得するGETメソッドを定義します。
サーブレットリスナーの操作プロセスを分析しましょう。
1。サーブレットリスナーの構成
現在、下の図に示すように、サーブレットの2種類のイベントには6種類のリスナーインターフェイスがあります。
特定のトリガーの状況は次のとおりです。
2。特定のリスナートリガープロセス
ここでイベント駆動型のプロセスを分析するための例として、ServletRequestattributeListenerを取り上げましょう。
まず第一に、httpservletrequestがsetattrilbuteメソッドを呼び出す場合、実際にはorg.apache.catalina.connector.request #setattrilbuteメソッドと呼ばれます。ソースコードを見てみましょう。
public void setAttribute(string name、object value){... //上記のロジックコードは省略されています//ここで、リスナーはnotifyattributeassigned(name、value、oldvalue); }以下は、notifyattributeassignedのソースコードです(文字列名、オブジェクト値、オブジェクトoldvalue)
private void notifyattributeassigned(string name、object value、object lognvalue){//コンテナオブジェクトリスナーからWebAppで定義されているリスナーのインスタンスオブジェクトを取得します[] = Context.GetAppLicationEventListeners(); if((reasters == null)||(ristens.length == 0)){return; } boolean交換=(oldvalue!= null); //関連するイベントオブジェクトServletRequestattributeEvent Event = null; if(flated){event = new servletrequestattributeevent(context.getServletContext()、getRequest()、name、oldvalue); } else {event = new ServletRequestattributeEvent(context.getServletContext()、getRequest()、name、value); } //すべてのリスナーを介した静けさリストを介して、(int i = 0; i <リスナー.length; i ++){if(!(!(!(i] servletrequestatttributeListener))の対応するイベントのリスナーを見つけます。 } //リスナー方法を呼び出してリスニング操作SERVLETREQUESTATTRIVITELISTENERリスナー=(servletRequestattributElistener)リスナー[i]; try {if(leffered){ristener.attributereplaced(event); } else {ristener.attributeadded(event); }} catch(throwable t){exceptionutils.handlethrowable(t); context.getLogger()。エラー(sm.getString( "coyoterequest.attributeevent")、t); //エラーバルブはこの例外を選択し、ユーザー属性に表示します。 }}}上記の例は、ServletRequestattributeListenerがどのように呼ばれるかを明確に示しています。ユーザーはリスナーインターフェイスを実装するだけです。サーブレットのリスナーは、サーブレットのライフサイクル全体を通して興味があるイベントをほとんどカバーしています。これらのリスナーを柔軟に使用すると、プログラムをより柔軟にすることができます。
3。包括的な例
たとえば、TVBの警察やギャングの映画を見たことがある場合、秘密がどのように機能するかがわかります。一般的に、警官には、敵に忍び込んで情報について尋ねるいくつかの秘密捜査官がいるかもしれません。秘密捜査官は、彼のリーダーの指示に完全に取り組んでいます。リーダーは、彼がどのような行動に従わなければならないかを言います。アクション時間が変更された場合、彼はすぐにアクションと協力する時間を変更する必要があります。リーダーが敵に侵入するために2人の秘密捜査官を派遣する場合、リーダーは抽象的なテーマと同等です。 Zhang San検査官は、2人の秘密捜査官Li SiとWan Wang Wuを送りました。 Zhang Sanは特定のテーマと同等であり、覆面捜査官は抽象観察者と同等です。これらの2人の覆面エージェントは、特定のオブザーバーであるLi SiとWang Wuです。問い合わせのアクションは、主題を登録するオブザーバーと同等です。次に、このクラス図は次のとおりです。
Java APIを使用して、次のようにコードの説明を実装します。
パッケージオブザーバー。 java.util.listをインポートします。 java.util.observableをインポートします。 java.util.observableをインポートします。 / ***説明:警察Zhang San*/ Public Class PoliceがObservable {Private String Time;公共警察(リスト<オブザーバー>リスト){super(); for(observer o:list){addobserver(o); }} public void change(string time){this.time = time; setChanged(); notifyobservers(this.time); }}パッケージオブザーバー。 java.util.observableをインポートします。 java.util.observerをインポートします。 / ** *説明:アンダーカバーA */パブリッククラスのアンダーカバーはオブザーバー{プライベート文字列時間; @Override public void Update(Observable o、object arg){time =(string)arg; system.out.println( "メッセージを受け取ったメッセージを除去し、アクション時間は次のとおりです。"+時間); }}パッケージオブザーバー。 java.util.observableをインポートします。 java.util.observerをインポートします。 / ** *説明:アンダーカバーb */パブリッククラスアンダーカバーは、オブザーバー{private string time; @Override public void Update(Observable o、object arg){time =(string)arg; system.out.println( "uncover bはメッセージを受信し、アクション時間は次のとおりでした:"+時間); }}パッケージオブザーバー。 java.util.arraylistをインポートします。 java.util.listをインポートします。 java.util.observerをインポートします。 / ***説明:test*/ public class client {/ ***@param args*/ public static void main(string [] args){undercovera o1 = new Undercovera(); undercoverb o2 = new undercoverb();リスト<オブザーバー> list = new ArrayList <>(); list.add(o1); list.add(o2);警察科目=新しい警察(リスト); subject.change( "02:25"); system.out.println( "===================================== Advanced ======================================================テスト実行結果:
アンダーカバーBはメッセージを受け取り、アクションタイムは次のとおりです。102:25アンダーカバーAはメッセージを受け取り、アクション時間は次のとおりです。
4。概要
オブザーバーパターンは、オブジェクト間の1対多くの関係を定義します。オブジェクト(オブザーバー)の状態が変更されると、それに依存するオブジェクトに通知されます。このようなビジネスシナリオでのパブリッシュサブスクライブ、Change-Update-UpDateに適用できます。
オブザーバーはゆるい結合法を使用します。オブザーバーはオブザーバーの詳細を知らないが、オブザーバーがインターフェイスを実装したことのみを知っている。
イベント駆動型モデルはより柔軟ですが、各イベントソースのリスナーとイベントをカスタマイズする必要があるため、システムの複雑さのコストも支払います。これにより、システムの負担が増加します。
オブザーバーモデルの中核は、最初にオブザーバーとオブザーバーを役割を区別し、位置付けることであり、それらは多くの関係です。実装の鍵は、オブザーバーとオブザーバーの間のつながりを確立することです。たとえば、オブザーバークラスにはオブザーバーの保存に使用されるセットがあり、検出されたものが変更されたときにすべてのオブザーバーに通知する必要があります。オブザーバーのコンストラクターでは、オブザーバーによって渡され、オブザーバーが所有するオブザーバー、つまりオブザーバーリストにも登録されます。
1。オブザーバーモードの利点:
(1)抽象的なテーマは抽象観察者にのみ依存しています
2。オブザーバーモードの短所:
(1)トピックが多数のオブザーバーによって登録されている場合、一部のオブザーバーの応答方法がブロックされている場合、すべてのオブザーバーに通知するにはより高い価格がかかります。通知プロセス全体がブロックされ、他のオブザーバーは時間内に通知できません。