
직접 의존성 : subject가 자신의 상태를 통지하려면 의존해야하는데, 직접 의존으로 구현하면 객체의 유연성과 재사용성이 떨어질 수 있다. 새로운 관찰자를 추가하거나 기존의 관찰자를 제거하려하면 subject 객체의 코드를 변경해야하기때문이다.일관성 문제 : subject가 자신의 상태변화를 모든 observer들에게 업데이트할때, 일관성이 깨질 수 있지만, observer 패턴은 subject가 observer들의 리스트를 갖고있고 각 observer마다 업데이트 함수를 호출함으로써 일관성을 보장할 수 있다.코드 복잡성 : 상태변화별로 다양한 event들을 처리해야하는데, observer 마다 각각 필요한 event를 처리하게하면 코드 가독성과 유지 보수성이 높아진다.public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
public interface Observer {
void update(float price);
}
Subject의 구현체는 이제 observer들의 리스트를 갖게되고 등록된 observer들에게 해당 주식 가격의 변동을 notifyObservers()를 통해 알리게 된다.
import java.util.ArrayList;
import java.util.List;
public class StockMarket implements Subject {
private List<Observer> observers;
private float price;
public StockMarket() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(price);
}
}
public void setPrice(float price) {
this.price = price;
notifyObservers();
}
}
observer의 구현체는 notify 받은 상태변화에 따라 각자의 기준에 맞게 notify 받은 변화를 처리하게 된다.
public class Investor implements Observer {
private float threshold;
public Investor(float threshold) {
this.threshold = threshold;
}
@Override
public void update(float price) {
if (price > threshold) {
System.out.println("매도해야 합니다!!!");
} else {
System.out.println("존버하십시오.");
}
}
}

객체간 결합도 : subject가 observer들로 하여금 함수를 호출시키는 방법과 달리, Pub/Sub의 경우는 Broker에 던져놓거나 모니터링하면 되기에 서로를 알 필요가 없다. 따라서 객체간 결합도가 더 낮다.동기/비동기 방식 : Pub/Sub 패턴은 Message Broker를 이용하므로 event를 Broker에 보내기만 하면 다른 task를 진행할 수 있다. 따라서 비동기 방식이라는 점에서 Observer 패턴과 차이가 있다.cross domain 허용 : Pub/Sub 패턴은 Broker라는 중간 매개체만 참조하면 되기에 다른 domain의 application에서 접근해 처리하는것이 가능하다.서비스 접근 흐름 제어 : proxy가 해당 객체 접근에 대한 모든 요청들을 처리하므로, 요청을 최대한 비동기적으로 처리할 수 있다. 예를 들어 client의 많은 요청으로 인해 DB 부하가 우려된다면, 이들의 흐름을 제어하여 적당한 traffic이 DB에 전해지도록 제어가 필요하다. 이때 proxy 객체에서 이를 제어해줄 수 있다.lazy initialization : 객체 생성 cost가 높다면, 일단 proxy를 참조하게 함으로써 해당 객체의 생성 시점을 미룰 수 있다.권한 확인 : 객체가 민감한 정보를 담고 있다면 client가 접근할 권한이 있는지 확인해야하는데 proxy에서 이를 확인해줌으로써 보안을 유지할 수 있다.코드 수정 최소화 : 해당 메소드의 실행에 필요한 전처리 기능들과 같이 수정이 필요할 경우, proxy 객체를 수정함으로써 구현 객체 변경없이 기능 수정이 가능하다.아래 코드를 통해, 실제 객체의 생성은 객체가 displayImage()라는 실제 기능을 할때까지 지연되고, 실제 메서드 실행도 proxy 객체를 통해 진행된다.
public interface Image {
void displayImage();
}
public class Real_Image implements Image {
private String fileName;
public Real_Image(String fileName) {
this.fileName = fileName;
loadFromDisk(fileName);
}
private void loadFromDisk(String fileName) {
System.out.println("Loading " + fileName);
}
@Override
public void displayImage() {
System.out.println("Displaying " + fileName);
}
}
public class Proxy_Image implements Image {
private Real_Image realImage;
private String fileName;
public Proxy_Image(String fileName) {
this.fileName = fileName;
}
@Override
public void displayImage() {
if (realImage == null) {
realImage = new Real_Image(fileName);
}
realImage.displayImage();
}
}
public class Proxy_Main {
public static void main(String[] args) {
Image image1 = new Proxy_Image("test1.png");
Image image2 = new Proxy_Image("test2.png");
image1.displayImage();
System.out.println();
image2.displayImage();
}
}

접근 방식의 표준화 : Collection의 구현 방법을 노출시키지 않으면서도, 어떤 종류의 집합체에 대해서도 element들을 조회할 수 있는 다형적인 코드를 만들 수 있다.
public interface Collection<E> extends Iterable<E> {
// Query Operations
/**
* Returns the number of elements in this collection. If this collection
* contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
* <tt>Integer.MAX_VALUE</tt>.
*
* @return the number of elements in this collection
*/
public interface Iterable<T> {
/**
* Returns an iterator over elements of type {@code T}.
*
* @return an Iterator.
*/
Iterator<T> iterator();
Java의 Collection들의 경우, 모두 Iterable을 상위 interface로 갖고 있기에 iterator()라는 메소드를 구현해야 한다. 해당 메소드의 코드는 다음과 같다.
public interface Iterator<E> {
/**
* Returns {@code true} if the iteration has more elements.
* (In other words, returns {@code true} if {@link #next} would
* return an element rather than throwing an exception.)
*
* @return {@code true} if the iteration has more elements
*/
boolean hasNext();
/**
* Returns the next element in the iteration.
*
* @return the next element in the iteration
* @throws NoSuchElementException if the iteration has no more elements
*/
E next();
/**
* Removes from the underlying collection the last element returned
* by this iterator (optional operation). This method can be called
* only once per call to {@link #next}. The behavior of an iterator
* is unspecified if the underlying collection is modified while the
* iteration is in progress in any way other than by calling this
* method.
*
* @implSpec
* The default implementation throws an instance of
* {@link UnsupportedOperationException} and performs no other action.
*
* @throws UnsupportedOperationException if the {@code remove}
* operation is not supported by this iterator
*
* @throws IllegalStateException if the {@code next} method has not
* yet been called, or the {@code remove} method has already
* been called after the last call to the {@code next}
* method
*/
default void remove() {
throw new UnsupportedOperationException("remove");
}
hasNext(), next(), remove() 등의 메소드들을 통해 Collection class의 element들을 조회하는 방식을 표준화할 수 있다.
https://zerocodings.com/22
https://jistol.github.io/software%20engineering/2018/04/11/observer-pubsub-pattern/
https://velog.io/@minsuk/Publish-Subscribe-패턴알림
https://coding-factory.tistory.com/711
https://esoongan.tistory.com/180
https://devlog-wjdrbs96.tistory.com/84
https://jusungpark.tistory.com/25