Java에서 Observer 디자인 패턴

Observer Pattern행위 디자인 패턴 중 하나입니다. Observer 디자인 패턴은 객체의 상태에 관심이 있고 상태가 변경될 때마다 알림을 받고 싶을 때 유용합니다. Observer 패턴에서 다른 객체의 상태를 관찰하는 객체를 Observer라고 하고, 관찰되는 객체는 Subject라고 합니다.

Observer 디자인 패턴

GoF에 따르면, Observer 디자인 패턴의 의도는;

객체 사이에 일대다의 종속성을 정의하여 하나의 객체의 상태가 변경되면 그에 종속된 모든 객체가 자동으로 알림을 받고 업데이트되도록 하는 것입니다.

주제에는 상태 변경을 통지하기 위한 관찰자 목록이 포함되어 있으므로 관찰자가 자체 상태를 등록 및 해제할 수 있는 메서드를 제공해야 합니다. 주제는 또한 모든 관찰자에게 변경 사항을 통지하는 메서드를 포함하고 있으며 통지하는 동시에 업데이트를 보낼 수 있거나 업데이트를 가져오는 또 다른 메서드를 제공할 수 있습니다. 관찰자는 감시할 객체를 설정하는 메서드와 주제가 어떠한 업데이트를 통지할 때 사용되는 다른 메서드를 가져야 합니다. Java는 java.util.Observable 클래스와 java.util.Observer 인터페이스를 통해 Observer 패턴을 구현하기 위한 내장 플랫폼을 제공합니다. 그러나 이는 구현이 매우 간단하며 자바는 클래스에서 다중 상속을 제공하지 않기 때문에 Observer 패턴을 구현하기 위해 클래스를 확장하고 싶지 않을 때가 많습니다. Java Message Service (JMS)는 Observer 디자인 패턴Mediator 패턴을 사용하여 응용 프로그램이 다른 응용 프로그램에 데이터를 구독하고 게시할 수 있도록 합니다. Model-View-Controller (MVC) 프레임워크도 주제가 되는 모델과 모델의 변경 사항을 통지받을 수 있는 관찰자인 뷰를 사용하여 Observer 패턴을 사용합니다.

Observer 패턴 Java 예제

옵저버 패턴 Java 프로그램 예제를 위해, 우리는 간단한 주제를 구현하고 옵저버들이 이 주제에 등록할 수 있도록 할 것입니다. 주제에 새로운 메시지가 게시되면, 등록된 모든 옵저버들에게 알림을 보내고 그들은 메시지를 소비할 수 있습니다. Subject의 요구 사항에 따라, 다음은 어떠한 구체적인 주제에 의해 구현되어야 하는 계약 메서드를 정의하는 기본 Subject 인터페이스입니다.

package com.journaldev.design.observer;

public interface Subject {

	//옵저버 등록 및 해제를 위한 메서드
	public void register(Observer obj);
	public void unregister(Observer obj);
	
	//변경 사항을 옵저버들에게 알리는 메서드
	public void notifyObservers();
	
	//주제로부터 업데이트를 받는 메서드
	public Object getUpdate(Observer obj);
	
}

다음으로, Observer에 대한 계약을 작성할 것입니다. Observer에게 Subject를 첨부할 수 있는 메서드와 Subject가 변경 사항을 알리기 위해 사용하는 다른 메서드가 있을 것입니다.

package com.journaldev.design.observer;

public interface Observer {
	
	//옵저버를 업데이트하는 메서드, Subject에 의해 사용됨
	public void update();
	
	//Subject에 첨부하여 관찰하기
	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 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 observersLocal = null;
		//동기화는 메시지를 받은 후 등록된 옵저버에게 알림을 보내지 않도록 보장하기 위해 사용됩니다
		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;
	}
	
	//주제에 메시지를 게시하는 메서드
	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;
	}

}

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

package com.journaldev.design.observer;

public class ObserverPatternTest {

	public static void main(String[] args) {
		//주제 생성
		MyTopic topic = new MyTopic();
		
		//관찰자 생성
		Observer obj1 = new MyTopicSubscriber("Obj1");
		Observer obj2 = new MyTopicSubscriber("Obj2");
		Observer obj3 = new MyTopicSubscriber("Obj3");
		
		//주제에 관찰자 등록
		topic.register(obj1);
		topic.register(obj2);
		topic.register(obj3);
		
		//주제에 관찰자 연결
		obj1.setSubject(topic);
		obj2.setSubject(topic);
		obj3.setSubject(topic);
		
		//사용 가능한 업데이트 확인
		obj1.update();
		
		//이제 주제로 메시지를 보냅니다
		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

Java Observer Pattern 클래스 다이어그램

Observer 디자인 패턴은 발행-구독 패턴으로도 불립니다. 그것의 몇 가지 구현 사례는 다음과 같습니다;

  • Swing에서 java.util.EventListener
  • javax.servlet.http.HttpSessionBindingListener
  • javax.servlet.http.HttpSessionAttributeListener

이것이 java의 Observer 디자인 패턴에 관한 전부입니다. 마음에 드셨기를 바랍니다. 의견을 나누고 다른 사람들과 공유하여 사랑을 나누어 주세요.

Source:
https://www.digitalocean.com/community/tutorials/observer-design-pattern-in-java