옵서버 패턴은 데이터의 변경이 발생되었을 경우
상대 클래스나 객체에 의존하지 않으면서 데이터 변경을 통보하고자 할 때 유용하다.
예를 들면 새로운 파일이 추가되거나 기존 파일이 삭제되었을 때
여러 프로그램에게 동시에 알려주어야 모든 프로그램이 그 최신 내역을 반영할 수 있다.
예를 들어보자
만약 회원들의 정보를 보관하는
Member 클래스와
Member들의 리스트를 출력해주는 Member View 클래스가 존재한다고 하였을 때,
다음과 같이 Member 객체가 추가 될 때 마다 Member View를 업데이트 해줄 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | // 멤버클래스 public class Member { private List<String> memberNames = new ArrayList<>(); private MemberView memberView; public List<String> getMemberNames() { return memberNames; } public void addMemberName(String memberName) { this.memberNames.add(memberName); memberView.update(); } public void setMemberView(MemberView memberView) { this.memberView = memberView; } } // 멤버 뷰 클래스 public class MemberView { private Member member; public MemberView(Member member) { this.member = member; } public void update() { displayMember(member.getMemberNames()); } private void displayMember(List<String> members) { Stream.of(members).forEach((data) -> { System.out.println(data); }); } } // Main 클래스 public class Main { public static void main(String args[]) { Member member = new Member(); MemberView view = new MemberView(member); member.setMemberView(view); member.addMemberName("babo"); member.addMemberName("chun jae"); member.addMemberName("korea"); } } | cs |
하지만 위에 경우에는 다음과 같은 문제가 있다.
현재는 하나의 뷰만 받을 수 있어서 Member의 값이 변경되어도
리스트를 출력하는 MemberView 클래스에게만 업데이트 통보를하는 구조로 되어있어 새로운 뷰를 추가할 수 없다.
핵심적인 문제는 멤버의 변경여부를 통보하는 클래스가 변경된다 하더라도
멤버의 내용은 그대로 사용할 수 있어야 한다는 점이다.
다음과 같이 변경해보자
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | // 관리대상을 바라보고 있는 Observer 인터페이스 public interface Observer { void update(); void display(List<Person> members); } // 멤버 객체의 변화를 지켜보고 있다가 변경시 팀원의 리스트를 출력하는 Observer public class MemeberLeaderView implements Observer { private Member member; public MemeberLeaderView(Member member) { this.member = member; } @Override public void update() { display(member.getMemberNames()); } @Override public void display(List<Person> members) { members.stream().forEach((data) -> { if (data.isLeader()) { System.out.println("leader is : " + data.getName()); } }); } } // 멤버 객체의 변화를 지켜보고 있다가 변경시 팀장을 출력하는 Observer public class MemberView implements Observer { private Member member; public MemberView(Member member) { this.member = member; } @Override public void update() { display(member.getMemberNames()); } @Override public void display(List<Person> members) { members.stream().forEach((data) -> { if (!data.isLeader()) { System.out.println("teamone name : " + data.getName()); } }); System.out.println(); } } // 객체 대상이 되는 객체의 subject 클래스 public abstract class Subject { private List<Observer> observers = new ArrayList<>(); public void attach(Observer view) { observers.add(view); } public void detach(Observer view) { observers.remove(view); } public void notifiyObservers() { observers.stream().forEach(data -> { data.update(); }); } } // 멤버 클래스 public class Member extends Subject { private List<Person> member = new ArrayList<>(); public List<Person> getMemberNames() { return member; } public void addMemberName(Person person) { this.member.add(person); notifiyObservers(); } } // person public class Person { private boolean isLeader; private String name; public Person(String name, boolean isLeader) { this.isLeader = isLeader; this.name = name; } public boolean isLeader() { return isLeader; } public String getName() { return name; } } // main 클래스 public class Main { public static void main(String args[]) { Member member = new Member(); member.attach(new MemeberLeaderView(member)); member.attach(new MemberView(member)); member.addMemberName(new Person("babo", true)); member.addMemberName(new Person("chun jae", false)); member.addMemberName(new Person("korea", false)); } } | cs |
관찰대상 (subject)는 관찰자 observer들의 항목을 가지고 있으며
subject가 변경되었을 때 observer들에게 자신의 변경 사실을 통보하는 구조로 변경하였다.
하나의 관리대상이 아닌 여러 관리대상을 가지고 관리할 수 있도록
observer를 인터페이스를 구현하였다.
실제로
쿼리박스-S 프로젝트를 진행하면서
여러 쿼리박스-S 프로젝트를 진행하면서
API를 subject, 각 사용자들의 쿼리박스-S를 observer로서 지정하여서 사용하였다.
api의 값이 변경될 경우 각 쿼리박스들에게 변경사실을 통보해줌으로써 쿼리박스-S들은 자신에 변경된 정책을 갱신할 수 있도록 설계한 적이 있었다.
잘만 활용하면 좋은 패턴인 것 같다.
'JAVA > Design Pattern' 카테고리의 다른 글
커맨드 패턴 (command pattern) (0) | 2018.05.28 |
---|---|
커맨드 패턴 (command pattern) (0) | 2018.05.27 |
데코레이트 패턴 (Decorator Pattern) (0) | 2018.05.27 |
템플릿 메서드 패턴 (Template method) (0) | 2018.05.27 |
팩토리 메서드 패턴 (Factory method) (0) | 2018.05.27 |