1. 옵저버 모드의 정의 :
옵저버 모드를 구독 출판 모드라고도하며 대상 객체는 모든 관찰자 객체를 관리하고 상태가 변경 될 때 알림을 적극적으로 발행합니다. 이것은 일반적으로 각 관찰자의 방법을 호출하여 달성됩니다. 이 패턴은 일반적으로 이벤트 처리 시스템에서 사용됩니다.
1. 관찰자 패턴의 일반적인 구조
먼저 관찰자 패턴에 대한 클래스 다이어그램 설명을 살펴 보겠습니다.
옵저버 모드의 역할은 다음과 같습니다.
제목 (초록 주제 인터페이스) : 추가, 삭제, 알림 등을 포함하여 테마 클래스의 관찰자 목록에서 일련의 작업을 정의합니다.
구체적인 주제 (특정 주제 클래스) :
Observer (Abstract Observer Interface) : 주제 클래스 업데이트 상태에서 작업을 수락하도록 관찰자를 정의합니다.
ConcreteObserver (특정 관찰자 클래스) : 관찰자 인터페이스 별 업데이트 주제 클래스 알림과 같은 논리를 구현합니다.
이 클래스 다이어그램에서 테마 클래스가 관찰자 인터페이스를 구현하는 클래스 목록을 유지한다는 것을 알 수 있습니다. 테마 클래스는이 목록을 사용하여 관찰자에 일련의 추가, 삭제 및 수정을 수행합니다. Observer 클래스는 또한 업데이트 방법을 적극적으로 호출하여 테마 클래스의 상태 업데이트 정보를 이해할 수 있습니다.
위의 클래스 다이어그램은 기본 관찰자 패턴 아이디어 만 설명하며 많은 단점이 있습니다. 예를 들어, 관찰자로서 특정 주제 등을 적극적으로 구독 할 수도 있습니다. 다음 예제는 특정 비즈니스 논리를 적용하기 위해 몇 가지 변경을 수행합니다.
2. 관찰 모드 예제
우리는 관찰자가 주제를 적극적으로 구독하거나 취소 할 수있는 관찰자와 주제 클래스를 구축합니다. 테마 범주는 테마 관리자가 균일하게 관리합니다. 다음은 클래스 다이어그램입니다.
주제:
공개 인터페이스 주제 {// 관찰자 공개 void 레지스터 등록 (Observer Observer); // 옵저버 공개 void 제거 제거 (Observer Observer); // 모든 관찰자에게 공개 공공 보이드 NotifyObservers (); // 주제 클래스에 의해 게시 될 메시지를 가져옵니다. 공개 문자열 getMessage ();} ConcertESubject : 공개 클래스 MySubject 구현 주제 {private list <Sebserver> 옵저버; 개인 부울 변경; 개인 문자열 메시지; // 객체 잠금, 관찰자 목록 개인 최종 개체 mutex = new Object (); public mysubject () {observers = new arraylist <serferver> (); 변경 = 거짓; } @override public void Register (Observer Observer) {if (옵저버 == null) 던지기 nullpointerexception (); // 반복하지 않도록 인증 if (! observers.crantains (Observer)) Observers.add (옵저버); } @override public void remove (Observer Observer) {Observers.remove (Observer); } @override public void notifyobservers () {// temp list list <Secterver> tempobservers = null; 동기화 된 (mutex) {if (! Changed) return; tempobservers = new arraylist <> (this.observers); this.changed = false; } for (Observer obj : tempobservers) {obj.update (); }} // 테마 클래스가 새 메시지를 게시합니다. 공개 void makechanged (문자열 메시지) {System.out.println ( "주제는 변경합니다 :" + 메시지); this.message = 메시지; this.changed = true; NotifyObservers (); } @override public String getMessage () {return this.message; }}ConcertesUbject가 업데이트를 할 때 목록의 모든 관찰자가 알리고 관찰자 업데이트 방법이 호출되어 알림을 수신 한 후 논리를 구현합니다. NotifyObservers의 동기화 블록을 여기에서 확인하십시오. 멀티 스레딩의 경우, 주제 클래스에 알림을 게시 할 때 다른 스레드별로 관찰자 목록을 추가하고 삭제하지 않기 위해 동기화 블록에 임시 목록이 사용되어 현재 관찰자 목록을 얻습니다.
주제 관리 : 테마 클래스 관리자
공개 클래스 주제 관리 {// 레코드 이름 -MAM Private Map <String, subject> subjectList = new Hashmap <String, goudric> (); public void addSubject (문자열 이름, 제목 주제) {istourlist.put (이름, 주제); } public void addSubject (주제 주제) {giversitylist.put (istod.getClass (). getName (), aversity); } 공개 주제 GetSubject (문자열 주제 이름) {return subjectList.get (gourdeName); } public void 제거 (문자열 이름, 제목 주제) {} public void removeSubject (주제 주제) {} // Singleton Private SubjectManagement () {} 공개 정적 주제 관리 getInstance () {return subjecmanagementInstance.instance; } private static class subjectmanagementInstance {정적 최종 주제 관리 인스턴스 = 새로운 주제 관리 (); }}주제 관리자의 목적은 관찰자가 주제를 구독 할 때 주제의 인스턴스 객체를 얻는 것입니다.
관찰자:
공개 인터페이스 옵저버 {public void update (); public void setSubject (주제 주제);} concerTeObserver : 공개 클래스 myObserver는 관찰자 {개인 주제 주제; // 농축물 주제에서 알림 메시지를 가져옵니다 @override public void update () {문자열 메시지 = giver.getMessage (); System.out.println ( "제목에서" + aversity.getClass (). getName () + "메시지 :" + 메시지); } @override public void setSubject (주제 주제) {this.subject = subject; } // subcirbe 일부 주제 공개 void 구독 (문자열 주제 이름) {giversitymanagement.getInstance (). } // 공개 void cancelsubscribe (string subjectName) {giversitymanagement.getInstance (). getSubject (istourname) .remove (this); }} 테스트 : 우리는 주제 수업과 관찰자를 작가와 독자로 추상화합니다.
공개 클래스 관찰 {private static mysubject Writer; @beforeclass public static void setupbeforeclass ()는 예외 {writer = new mysubject (); // Linus istoublemagement.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는 모든 청취자 가이 인터페이스를 상속해야한다고 규정합니다.
공개 클래스 eventObject는 Java.io.serializable을 구현합니다. {private static final long serialversionuid = 5516075349620653480L; /*** 이벤트가 처음 발생한 개체. */ 보호 된 과도 객체 소스; /*** 프로토 타입 이벤트를 구성합니다. * * @param 이벤트가 처음 발생한 개체를 소스하십시오. * @exception 불법 행위 exception 소스가 null 인 경우. */ public eventObject (Object Source) {if (source == null) 새로운 불법 행위 exception ( "null 소스"); this.source = source; } /*** 이벤트가 처음 발생한 개체. * * * @이벤트가 처음 발생한 개체. */ public object getSource () {return source; } /***이 EventObject의 문자열 표현을 반환합니다. * * @return이 eventObject의 문자열 표현. */ public String toString () {return getClass (). getName () + "[소스 =" + 소스 + "]; }}EvenObject는 JDK가 지정한 통합 이벤트 소스입니다. EvenObject 클래스는 이벤트 소스와 이벤트 소스를 얻는 GET 메소드를 정의합니다.
서블릿 리스너의 작동 프로세스를 분석하겠습니다.
1. 서블릿 리스너의 구성
현재 아래 그림과 같이 서블릿에는 두 가지 유형의 이벤트에 대한 6 가지 유형의 리스너 인터페이스가 있습니다.
특정 트리거 상황은 다음과 같습니다.
2. 특정 청취자 트리거링 프로세스
여기에서 이벤트 중심 프로세스를 분석하기위한 예로 ServletRequestattributeListener를 사용해 봅시다.
우선, httpservletrequest가 setattrilbute 메소드를 호출 할 때 실제로 org.apache.catalina.connector.request#setattrilbute 메서드라고합니다. 소스 코드를 살펴 보겠습니다.
public void setattribute (문자열 이름, 객체 값) {... // 위의 논리 코드가 생략되었습니다. }다음은 NotifyAttributeAssigned의 소스 코드입니다 (문자열 이름, 객체 값, 개체 OldValue)
private void notifyAttributeAssigned (문자열 이름, 객체 값, 객체 OldValue) {// 컨테이너 개체 리스너에서 webApp에 정의 된 리스너의 인스턴스 개체를 가져옵니다 [] = context.getApplicationEventListeners (); if ((청취자 == null) || (리스너 .length == 0)) {return; } 부울 대체 = (OldValue! = null); // 관련 이벤트 생성 객체 servletrequestattributeevent event = null; if (대체) {event = new servletrequestattributeevent (context.getServletContext (), getRequest (), 이름, OldValue); } else {event = new servletrequestattributeevent (context.getServletContext (), getRequest (), 이름, 값); } // 모든 리스너 목록을 통한 평온함 (int i = 0; i <리스너.length; i ++) {if (! (! instanceof servletrequesttributeListener)) {계속; } // 청취 작업을 구현하기 위해 리스너 메소드를 호출하여 servletrequestattributeListener Listener = (servletRequestAttributeListener) 리스너 [i]; try {if (대체) {Listener.attributeReplaced (이벤트); } else {Listener.attributeadded (이벤트); }} catch (Throwable t) {exceptions.handleThrowable (t); context.getLogger (). ERROR (sm.getString ( "coyoteRequest.attributeevent"), t); // Error Valve는이 예외를 선택하여 사용자 속성에 표시합니다. }}}위의 예는 ServletRequestAttriptionErsener가 어떻게 호출되는지 명확하게 보여줍니다. 사용자는 리스너 인터페이스 만 구현하면됩니다. 서블릿의 청취자는 서블릿의 전체 수명주기에 관심이있는 이벤트를 거의 다룹니다. 이러한 청취자를 유연하게 사용하면 프로그램을보다 유연하게 만들 수 있습니다.
3. 포괄적 인 예
예를 들어, TVB의 경찰 및 갱스터 영화를 보았다면 비밀이 어떻게 작동하는지 알게 될 것입니다. 일반적으로 경찰관은 적에게 몰래 들어가서 정보에 대해 문의하는 몇몇 비밀 요원이있을 수 있습니다. 비밀 요원은 전적으로 그의 지도자의 지시에 따라 작동합니다. 지도자는 자신이 따라야 할 행동을 말합니다. 액션 시간이 변경되면 그는 행동과 협력하는 시간을 즉시 변경해야합니다. 리더가 적을 침공하기 위해 두 명의 비밀 요원을 보내면 리더는 추상 테마와 동일합니다. Zhang San 검사관은 두 명의 비밀 요원 Li Si와 Wan Wang Wu를 보냈습니다. Zhang San은 특정 테마와 동일하며 비밀 요원은 추상 관찰자와 동일합니다. 이 두 가지 비밀 요원은 Li Si와 Wang Wu이며, 이는 특정 관찰자입니다. 문의 조치는 주제를 등록하는 관찰자와 동일합니다. 이 클래스 다이어그램은 다음과 같습니다.
Java API를 사용하여 다음과 같이 코드 설명을 구현합니다.
패키지 관찰자; Java.util.list 가져 오기; java.util.observable import; java.util.observable import; / ***설명 : 경찰 Zhang San*/ Public Class 경찰은 관찰 가능한 {개인 문자열 시간; 공공 경찰 (List <Observer> List) {Super (); for (observer o : list) {addobserver (o); }} public void 변경 (문자열 시간) {this.time = time; setChanged (); NotifyObservers (this.time); }} 패키지 관찰자; java.util.observable import; import java.util.observer; / ** *설명 : 언더 커버 a */ public class Undercovera는 관찰자 {개인 문자열 시간; @override public void update (Observable O, Object Arg) {time = (string) arg; System.out.println ( "A를받지 못하고 메시지를받지 못하고 액션 시간은 다음과 같습니다."+time); }} 패키지 관찰자; java.util.observable import; import java.util.observer; / ** *설명 : 언더 커버 b */ 공개 클래스 언더 커버는 관찰자 {개인 문자열 시간; @override public void update (Observable O, Object Arg) {time = (string) arg; System.out.println ( "Uncover B는 메시지를 받았으며 액션 시간은 다음과 같습니다."+time); }} 패키지 관찰자; java.util.arraylist 가져 오기; Java.util.list 가져 오기; import java.util.observer; / ***설명 : 테스트*/ public class client {/ ***@param args*/ public static void main (string [] args) {Undercovera o1 = new Undercovera (); Undercoverb o2 = 새로운 밑면 (); List <cecterver> list = new ArrayList <> (); list.add (o1); list.add (o2); 경찰 과목 = 새로운 경찰 (목록); 대상 체인 ( "02:25"); System.out.println ( "======================== 정보가 노출되어있어서 액션 시간은 다음과 같습니다. 고급 ===================================================== 테스트 실행 결과 :
언더 커버 B는 메시지를 받았으며 액션 시간은 다음과 같습니다. 02:25 메시지를 수신하고 액션 시간은 다음과 같습니다. 02:25 =========== 메시지의 노출로 인해 액션 시간이 사전에 있습니다 ========== UNDERCOVER B가 메시지를 받았으며, 01:05는 메시지를 받았습니다.
4. 요약
관찰자 패턴은 물체 사이의 일대일 관계를 정의합니다. 객체 (관찰자)의 상태가 변경되면 이에 의존하는 객체에 알 수 있습니다. 그러한 비즈니스 시나리오에서 Publish-Subscribe, Change-Update Update에 적용 할 수 있습니다.
관찰자는 느슨한 커플 링 방법을 사용합니다. 관찰자는 관찰자의 세부 사항을 알지 못하지만 관찰자가 인터페이스를 구현했다는 것을 알고 있습니다.
이벤트 중심 모델은 더 유연하지만 각 이벤트 소스에 대한 리스너 및 이벤트를 사용자 정의해야하므로 시스템의 부담을 증가시켜야하기 때문에 시스템 복잡성 비용을 지불합니다.
관찰자 모델의 핵심은 먼저 역할을 구별하고 관찰자와 관찰자를 배치하는 것입니다. 구현의 핵심은 관찰자와 관찰자 사이의 연결을 설정하는 것입니다. 예를 들어, 관찰자 클래스에는 관찰자를 저장하는 데 사용되는 세트가 있으며 감지 된 물건이 변경되면 모든 관찰자에게 알리려면 모든 관찰자에게 알릴 수 있습니다. 관찰자의 생성자에서 관찰자가 통과하고 관찰자가 소유 한 관찰자 목록, 즉 관찰자 목록에 등록합니다.
1. 관찰자 모드의 장점 :
(1) 추상 주제는 추상 관찰자에만 의존합니다 (2) 관찰자 모드는 방송 통신을 지원합니다 (3) 관찰자 모드는 정보 생성 계층과 응답 계층을 분리합니다.
2. 관찰자 모드의 단점 :
(1) 많은 수의 관찰자가 주제를 등록하는 경우 모든 관찰자에게 알리는 데 더 높은 가격이 소요됩니다 (2) 일부 관찰자의 응답 방법이 차단되면 전체 알림 프로세스가 차단되고 다른 관찰자에게 제 시간에 통지 될 수 없습니다.