웹사이트 검색

Java의 관찰자 디자인 패턴


관찰자 패턴은 행동 디자인 패턴 중 하나입니다. 관찰자 디자인 패턴은 개체의 상태에 관심이 있고 변경 사항이 있을 때마다 알림을 받고 싶을 때 유용합니다. 옵저버 패턴에서 다른 객체의 상태를 감시하는 객체를 Observer라고 하고 감시하고 있는 객체를 Subject라고 합니다.

옵저버 디자인 패턴

개체 간의 일대다 종속성을 정의하여 한 개체의 상태가 변경되면 모든 종속 항목에 알림이 자동으로 전송되고 업데이트됩니다.

Subject는 상태 변경을 알리는 관찰자 목록을 포함하므로 관찰자가 스스로 등록 및 등록 해제할 수 있는 메서드를 제공해야 합니다. Subject에는 모든 관찰자에게 변경 사항을 알리는 방법도 포함되어 있으며 관찰자에게 알리는 동안 업데이트를 보내거나 업데이트를 얻을 수 있는 다른 방법을 제공할 수 있습니다. Observer는 개체를 감시하도록 설정하는 메서드와 Subject가 업데이트를 알리기 위해 사용할 또 다른 메서드를 가져야 합니다. Java는 java.util.Observable 클래스와 java.util.Observer 인터페이스를 통해 Observer 패턴을 구현하기 위한 내장형 플랫폼을 제공합니다. 그러나 구현이 정말 간단하고 대부분의 경우 Java가 클래스에서 다중 상속을 제공하지 않기 때문에 Observer 패턴을 구현하기 위해 클래스를 확장하는 것을 원하지 않기 때문에 널리 사용되지 않습니다. JMS(Java Message Service)는 중재자 패턴과 함께 관찰자 디자인 패턴을 사용하여 응용 프로그램이 데이터를 구독하고 다른 응용 프로그램에 게시할 수 있도록 합니다. 모델-뷰-컨트롤러(MVC) 프레임워크는 또한 모델이 주제이고 뷰가 모델에 대한 변경 사항에 대한 알림을 받기 위해 등록할 수 있는 관찰자인 관찰자 패턴을 사용합니다.

관찰자 패턴 자바 예제

관찰자 패턴 자바 프로그램 예제의 경우 간단한 주제를 구현하고 관찰자는 이 주제에 등록할 수 있습니다. 새 메시지가 주제에 게시될 때마다 모든 레지스터 관찰자에게 알림이 전송되고 메시지를 사용할 수 있습니다. Subject의 요구 사항에 따라 구체적인 주체가 구현할 계약 메서드를 정의하는 기본 Subject 인터페이스가 있습니다.

package com.journaldev.design.observer;

public interface Subject {

	//methods to register and unregister observers
	public void register(Observer obj);
	public void unregister(Observer obj);
	
	//method to notify observers of change
	public void notifyObservers();
	
	//method to get updates from subject
	public Object getUpdate(Observer obj);
	
}

다음으로 우리는 Observer에 대한 계약을 생성할 것입니다. Subject를 Observer에 연결하는 방법과 Subject가 변경 사항을 알리는 데 사용하는 또 다른 방법이 있습니다.

package com.journaldev.design.observer;

public interface Observer {
	
	//method to update the observer, used by subject
	public void update();
	
	//attach with subject to observe
	public void setSubject(Subject sub);
}

이제 계약이 준비되었으므로 주제의 구체적인 구현을 진행하겠습니다.

package com.journaldev.design.observer;

import java.util.ArrayList;
import java.util.List;

public class MyTopic implements Subject {

	private List<Observer> observers;
	private String message;
	private boolean changed;
	private final Object MUTEX= new Object();
	
	public MyTopic(){
		this.observers=new ArrayList<>();
	}
	@Override
	public void register(Observer obj) {
		if(obj == null) throw new NullPointerException("Null Observer");
		synchronized (MUTEX) {
		if(!observers.contains(obj)) observers.add(obj);
		}
	}

	@Override
	public void unregister(Observer obj) {
		synchronized (MUTEX) {
		observers.remove(obj);
		}
	}

	@Override
	public void notifyObservers() {
		List<Observer> observersLocal = null;
		//synchronization is used to make sure any observer registered after message is received is not notified
		synchronized (MUTEX) {
			if (!changed)
				return;
			observersLocal = new ArrayList<>(this.observers);
			this.changed=false;
		}
		for (Observer obj : observersLocal) {
			obj.update();
		}

	}

	@Override
	public Object getUpdate(Observer obj) {
		return this.message;
	}
	
	//method to post message to the topic
	public void postMessage(String msg){
		System.out.println("Message Posted to Topic:"+msg);
		this.message=msg;
		this.changed=true;
		notifyObservers();
	}

}

옵저버를 등록 및 등록 취소하는 메서드 구현은 매우 간단합니다. 추가 메서드는 postMessage()입니다. 이 메서드는 클라이언트 애플리케이션에서 주제에 문자열 메시지를 게시하는 데 사용됩니다. 주제 상태의 변화를 추적하고 관찰자에게 알리는 데 사용되는 부울 변수에 주목하십시오. 이 변수는 업데이트가 없고 누군가가 notifyObservers() 메서드를 호출하는 경우 관찰자에게 잘못된 알림을 보내지 않도록 하기 위해 필요합니다. 또한 notifyObservers() 메서드에서 동기화를 사용하여 메시지가 주제에 게시되기 전에 등록된 관찰자에게만 알림이 전송되도록 합니다. 다음은 주제를 감시할 관찰자의 구현입니다.

package com.journaldev.design.observer;

public class MyTopicSubscriber implements Observer {
	
	private String name;
	private Subject topic;
	
	public MyTopicSubscriber(String nm){
		this.name=nm;
	}
	@Override
	public void update() {
		String msg = (String) topic.getUpdate(this);
		if(msg == null){
			System.out.println(name+":: No new message");
		}else
		System.out.println(name+":: Consuming message::"+msg);
	}

	@Override
	public void setSubject(Subject sub) {
		this.topic=sub;
	}

}

사용할 메시지를 가져오기 위해 Subject getUpdate() 메서드를 호출하는 update() 메서드의 구현에 주목하세요. 메시지를 update() 메서드의 인수로 전달하면 이 호출을 피할 수 있습니다. 다음은 주제 구현을 사용하는 간단한 테스트 프로그램입니다.

package com.journaldev.design.observer;

public class ObserverPatternTest {

	public static void main(String[] args) {
		//create subject
		MyTopic topic = new MyTopic();
		
		//create observers
		Observer obj1 = new MyTopicSubscriber("Obj1");
		Observer obj2 = new MyTopicSubscriber("Obj2");
		Observer obj3 = new MyTopicSubscriber("Obj3");
		
		//register observers to the subject
		topic.register(obj1);
		topic.register(obj2);
		topic.register(obj3);
		
		//attach observer to subject
		obj1.setSubject(topic);
		obj2.setSubject(topic);
		obj3.setSubject(topic);
		
		//check if any update is available
		obj1.update();
		
		//now send message to subject
		topic.postMessage("New Message");
	}

}

위의 프로그램을 실행하면 다음과 같은 결과를 얻습니다.

Obj1:: No new message
Message Posted to Topic:New Message
Obj1:: Consuming message::New Message
Obj2:: Consuming message::New Message
Obj3:: Consuming message::New Message

자바 옵저버 패턴 클래스 다이어그램

  • Swing의 java.util.EventListener
  • javax.servlet.http.HttpSessionBindingListener
  • javax.servlet.http.HttpSessionAttributeListener

여기까지가 Java의 Observer 디자인 패턴에 대한 전부입니다. 마음에 드셨기를 바랍니다. 댓글로 사랑을 나누고 다른 사람들과 공유하세요.