JAVA/Design Pattern

옵저버 패턴 (Observer Pattern)

반응형

옵서버 패턴은 데이터의 변경이 발생되었을 경우 

상대 클래스나 객체에 의존하지 않으면서 데이터 변경을 통보하고자 할 때 유용하다.

예를 들면 새로운 파일이 추가되거나 기존 파일이 삭제되었을 때

여러 프로그램에게 동시에 알려주어야 모든 프로그램이 그 최신 내역을 반영할 수 있다.

예를 들어보자

만약 회원들의 정보를 보관하는

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들은 자신에 변경된 정책을 갱신할 수 있도록 설계한 적이 있었다.

잘만 활용하면 좋은 패턴인 것 같다.

반응형