# 설명
- 객체 사이에 일대다의 의존관계가 존재하고, 어떤 객체의 상태가 변하면 그 객체에 의존성을 가진 다른 객체들은 변화됨을 받고 갱신 할 수 있도록 함
- 하나의 객체에 연동되는 여러 객체 집합이 있을때 변화에 대한 일관성은 유지하고, 객체간의 결합도는 낮게하기 위한 패턴
- data <-> view관계에서 많이 사용됨
## 안드로이드에서 사용된 옵저버 패턴
안드로이드를 개발하다보면, 여러 View들이 한 DATA를 바라보고 있는 경우가 있다. DATA가 바뀌는 경우는 여러가지가 존재한다고 가정해보자. 그렇다면 DATA를 바꿔주는 여러가지의 방법(함수)마다 View들을 update시키는 코드를 작성한다고 생각해보자. 10개의 View가 존재하고 10개의 데이터를 변경시키는 코드가 있다면 DATA가 변경되는 시점마다 View들을 다시 그려주는 코드를 작성해야한다.
// 로그인했으면 그에 맞는 View들을 다시 그려줌
public void login(){
// view_update : findByView<TextView>... header를 nickname으로 바꿔주는 코드
// view_update : findByView<TextView>... footer를 닉네임으로 바꿔주는 코드
// view_update : findByView<TextView>...
// view_update : findByView<TextView>...
// view_update : findByView<TextView>...
// view_update : findByView<TextView>...
}
물론 안드로이드를 처음 개발할때는 위와 같은일이 번거롭다는 생각은 들지 않을 수 있다. 그러나 프로젝트의 규모가 커지다 보면 몇몇 코드를 빠트릴 수도 있을 것이다.
이런경우 어떻게 하면 좋을까? View들이 DATA의 변화를 감지하고 변화할때마다 그에 맞는 일을 수행시켜주면 정말 좋을 것이다. 실제로LiveData와 ViewModel을 이용해서 그렇게 개발하는 걸 안드로이드 코드랩을 통해서 하기도 했다.
// Add observer for score
viewModel.score.observe(viewLifecycleOwner, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
위 코드를 보면, scoreText라는 view는 newScore라는 데이터를 관찰하고 있다가 newScore라는 데이터가 변경되면 알아서 view의 text를 바꿔준다. 즉 DATA를 어디서 바꾼거는 신경쓰지 않고, 데이터가 바뀐다면 알아서 바꿔주게 되는 것이다. 이렇게 옵저버 패턴을 사용하면 data의 변화를 감지하고 그에 맞는 일을 해주게 할 수 있다.
## 옵저버 패턴
먼저 Subject가 존재한다. 이 추상클래스(인터페이스도가능)는 Observer들을 ArrayList와 같은 형태로 관리하고 있으며 옵저버를 등록하고 지울 수 있다. 그리고 Notify()라는 기능을 구현하여 현재 관리되고 있는 Observer들에게 update()함수를 실행하도록 명한다. update()함수는 옵저버객체들이 구현하기나름이다. Notify()를 통해서 신호를 보내줄 뿐이다.
ConcreteSubject는 ConcreteObserver에게 알려주어야하는 상태가 변경될때 Subject에서 정의한 notify를 호출하도록 한다. 주로 ArrayList로 Observer를 관리한다. 상황에 따라서 Subject를 추상클래스로 만들어 ArrayList와 notify기능을 ConcreteSubject에서 구현하는게 아닌 Subject에서 구현할 수도 있다. 이번에 만든 샘플 코드는 그런 방법을 사용했다.
Observer인터페이스는 Subject의 변화에 관심을 가지는 객체이다. 갱신에 필요한 인터페이스를 정의한다.
ConcreteObserver는 정의된 인터페이스(update())를 구현한다.
# 예제
- 간단한 회사-유저 예제를 만들어 보았다.
- 방송회사에 유저들은 구독 및 구독해제를 신청할 수 있다.
- 방송회사는 새로운 알림(뉴스)가 나온다면 구독을 한 유저들에게 알림을 전달한다.
# 코드
## Observer
// Observer
public interface Observer {
public void update(Company company);
}
// User
public class User implements Observer{
String name;
public User(String name) {
this.name = name;
}
@Override
public void update(Company company) {
System.out.println("===================");
System.out.println(name+"님 새로운 알림이 도착했습니다.");
System.out.println(company.getMsg());
System.out.println("===================");
System.out.println();
}
}
## Company
// Company
public abstract class Company {
ArrayList<Observer> observers = new ArrayList<>();
public void addUser(Observer observer){
observers.add(observer);
}
public void removeUser(Observer observer){
observers.remove(observer);
}
public void notifyAllUser(){
observers.forEach(observer -> observer.update(this));
}
public abstract void publishItem(String msg);
public abstract String getMsg();
}
// BroadCastCompany
public class BroadCastCompany extends Company{
String msg;
String companyName;
BroadCastCompany(String name){
super();
companyName = name;
}
@Override
public void publishItem(String msg) {
this.msg = companyName + "에서 알림," + msg;
notifyAllUser();
}
@Override
public String getMsg() {
return msg;
}
}
# 테스트
public class Test {
public static void main(String[] args) {
Company kbs = new BroadCastCompany("KBS");
Observer hojun = new User("HoJun");
Observer bob = new User("Bob");
Observer tom = new User("Tom");
Observer sam = new User("Sam");
Observer martin = new User("R.martin");
kbs.addUser(hojun);
kbs.addUser(bob);
kbs.addUser(tom);
kbs.addUser(sam);
kbs.addUser(martin);
kbs.publishItem("[기상예보] 내일 많은비가 예상됩니다.");
}
}
# 마치며
예제 코드를 만들어 봤는데 Company-BroadCastCompany로 만드는 것은 완벽한 예제라고 보기는 어려울 것 같다. 또 Company를 추상클래스로 선언했었다. 이것보다 Publisher(인터페이스) - Company와 같이 만는 것이 더 좋을 거 같다.
즉 인터페이스에서 notify(), addUser(), removeUser()와 같은것만 정의해주고 Company내에서 이 기능을 구현하고 Observer객체들을 관리하는게 더 깔끔해 보인다. 리팩토링의 기회가 주어진다면 코드를 다시 수정해야겠다!
# 참고
'• 독서 > Design Pattern' 카테고리의 다른 글
[디자인패턴] 퍼사드 패턴(Facade Pattern) (0) | 2022.10.11 |
---|---|
[디자인 패턴] 메멘토 패턴(Memento Pattern) (0) | 2022.10.10 |
[디자인 패턴] 스테이트 패턴(State Pattern) (0) | 2022.10.02 |
[디자인패턴] 어댑터 패턴(Adapter Pattern) (0) | 2022.10.01 |
[디자인 패턴] 컴포지트 패턴(Composite Pattern) (0) | 2022.09.30 |