상태를 가지고 있는 주체 객체 & 상태의 변경을 알아야 하는 관찰 객체
-
서로의 정보를 주고받는 과정에서 정보의 단위가 클수록, 객체들의 규모가 클수록 복잡성이 증가하게 됨
-
이때 가이드라인을 제시해줄 수 있는 것이 '옵저버 패턴'
-
주체가 어떤 객체(subject)의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 메서드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인 패턴
- 주체: 객체의 상태 변화를 보고 있는 관찰자
- 옵저버들: 이 객체의 상태 변화에 따라 전달되는 메서드 등을 기반으로 '추가 변화사항'이 생기는 객체들을 의미함
- 주체와 객체를 따로 두지 않고 상태가 변경되는 객체를 기반으로 구축하기도 함
- 옵저버 패턴을 활용한 서비스 예시:
트위터
- 내가 어떤 사람인 주체를 '팔로우'했다면 주체가 포스팅을 울리게 되면 알림이 '팔로워'에게 가야함
- 옵저버 패턴은 주로
이벤트 기반 시스템
에 사용하며MVC(Model-View-Controller) 패턴
에도 사용됨- 예를 들어 주체라고 볼 수 있는 모델(model)에서 변경 사항이 생겨 update() 메서드로 옵저버인 뷰에 알려주고 이를 기반으로 컨트롤러(controller) 등이 작동하는 것
Observer들을 관리하는 메소드를 가지고 있음
- 옵저버 등록(add), 제외(delete), 옵저버들에게 정보를 알려줌(notifyObserver)
public interface Publisher {
public void add(Observer observer);
public void delete(Observer observer);
public void notifyObserver();
}
정보를 업데이트(update)
public interface Observer {
public void update(String title, String news); }
Publisher를 구현한 클래스로, 정보를 제공해주는 퍼블리셔가 됨
public class NewsMachine implements Publisher {
private ArrayList<Observer> observers;
private String title;
private String news;
public NewsMachine() {
observers = new ArrayList<>();
}
@Override
public void add(Observer observer) {
observers.add(observer);
}
@Override
public void delete(Observer observer) {
int index = observers.indexOf(observer);
observers.remove(index);
}
@Override
public void notifyObserver() {
for(Observer observer : observers) {
observer.update(title, news);
}
}
public void setNewsInfo(String title, String news) {
this.title = title;
this.news = news;
notifyObserver();
}
public String getTitle() { return title; }
public String getNews() { return news; }
}
Observer를 구현한 클래스들로, notifyObserver()를 호출하면서 알려줄 때마다 Update가 호출됨
public class EventSubscriber implements Observer {
private String newsString;
private Publisher publisher;
public EventSubscriber(Publisher publisher) {
this.publisher = publisher;
publisher.add(this);
}
@Override
public void update(String title, String news) {
newsString = title + " " + news;
display();
}
public void withdraw() {
publisher.delete(this);
}
public void display() {
System.out.println("이벤트 유저");
System.out.println(newsString);
}
}
-
상속
- 상속(extends)은 자식 클래스가 부모 클래스의 메서드 등을 상속받아 사용하며 자식 클래스에서 추가 및 확장을 할 수 있는 것
- 이로 인해 재사용성, 중복성의 최소화가 이루어짐
-
구현
- 부모 인터페이스(interface)를 자식 클래스에서 재정의하여 구현하는 것을 말함
- 상속과는 달리 반드시 부모 클래스의 메서드를 재정의하여 구현해야 함
-
상속과 구현의 차이
- 상속은 일반 클래스, abstract 클래스를 기반으로 구현함
- 구현은 인터페이스를 기반으로 구현함
- 프록시 객체를 통해 구현할 수도 있음
-
프록시(proxy) 객체는 어떠한 대상의 기본적인 동작(속성 접근, 할당, 순회, 열거, 함수 호출 등)의 작업을 가로챌 수 있는 객체를 뜻하며, 자바스크립트에서 프록시 객체는 두 개의 매개변수를 가짐
-
target: 프록시할 대상
-
handler: target동작을 가로채고 어떠한 동작을 할 것인지가 설정되어 있는 함수
-
프록시 객체를 구현한 코드
const handler = {
get: function (target, name) {
return name === "name" ? `${target.a} ${target.b}` : target[name];
},
};
const p = new Proxy({ a: "KUNDOL", b: "IS AUMUMU ZANGIN" }, handler);
console.log(p.name); // KUNDOL IS AUMUMU ZANGIN
- new Proxy()로 a와 b 속성을 가지고 있는 객체와 handler 함수를 매개변수로 넣고 p라는 변수를 선언함
- 이후 p의 name 속성을 참조하니 a와 b라는 속성밖에 없는 객체가 handler의 "name"이라는 속성에 접근할 때 a와 b를 합쳐서 문자열을 만들라"는 로직에 따라 어떤 문자열을 만듬
- 이렇게 name 속성 등 특정 속성에 접근할 때 그 부분을 가로채서 어떠한 로직을 강제할 수 있는 것이 프록시 객체임
function createReactiveObject(target, callback) {
const proxy = new Proxy(target, {
set(obj, prop, value) {
if (value !== obj[prop]) {
const prev = obj[prop];
obj[prop] = value;
callback(`${prop}가 [#{prev}] >> [${value}] 로 변경되었습니다. `);
}
return true;
},
});
return proxy;
}
const a = {
형규: "솔로",
};
const b = createReactiveObject(a, console.log);
b.형규 = "솔로";
b.형규 = "커플";
// 형규가 [솔로] >> [커플]로 변경되었습니다.