ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [디자인패턴] 옵저버 패턴 (Observer Pattern) - Java 내장 객체 사용
    IT, 프로그래밍/Design Patterns 2017. 10. 4. 13:07

    지난 시간에는 옵저버 패턴의 기본 개념과 구현하는 것에 대해서 알아보았습니다.


    우리가 알아본 옵저버 패턴은 흔하게 사용하는 디자인 패턴 중에 하나입니다.


    특히 이벤트 처리에 사용되는데요.


    자바의 swing 프로그래밍을 접해 보신 분들은 eventListener에 대해서 익숙하실 겁니다. 


    이벤트리스너를 통해 우리는 특정한 입력 신호가 들어 올 때 마다, 원하는 행동을 하도록 할 수 있습니다. 이 이벤트 처리 과정은 옵저버 패턴을 사용해 구현하였습니다. 


    컴포넌트에 eventListener라는 옵저버 부착하면, 해당 컴포넌트에서 발생하는 이벤트를 감시할 수 있습니다. 


    자바에서는 이 패턴을 편하게 사용할 수 있도록 API로 만들어 지원합니다.


    Observer 인터페이스와 Observable 클래스를 사용해서 지난번에 썼던 예제를 구현해 보겠습니다.






    DisplayElement 인터페이스



    1
    2
    3
    4
    5
    6
    7
    8
    9
    package javaObserver;
     
     
    public interface DisplayElement {
     
        public void display();
        
    }
     
    cs



    Company 클래스



    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
    package javaObserver;
     
    import java.util.Observable;
     
    public class Company extends Observable {
     
        private String photoUrl;
        private String content;
     
        public Company() {
        }
     
        public void messageChanged() { // 새로운 소식이 들어왔다고 알려줌 (상태가 변했다고 알려주는 메소드)
     
            setChanged();
            notifyObservers();
        }
     
        public void setMessage(String photoUrl, String content) { // 새로운 소식이 들어오는 메소드
     
            this.photoUrl = photoUrl;
            this.content = content;
            messageChanged();
     
        }
     
        public String getPhotoUrl() {
            return photoUrl;
        }
     
        public String getContent() {
            return content;
        }
        
        
     
    }
     
    cs



    Subscriber 클래스



    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
    package javaObserver;
     
    import java.util.Observable;
    import java.util.Observer;
     
    public class Subscriber implements Observer, DisplayElement{
     
        Observable observable;
        private String photoUrl;
        private String content;
     
        public Subscriber(Observable observable) {
     
            this.observable = observable; 
            observable.addObserver(this); //옵저버로 등록한다.
        }
     
        @Override
        public void display() { //받은 소식을 화면에 나타내어주는 메소드
            // TODO Auto-generated method stub
            System.out.println("1번째 구독자");
            System.out.println("photo link : " + photoUrl);
            System.out.println("content : " + content);
            System.out.println("\n");
        }
     
        @Override
        public void update(Observable obs, Object arg) { //새로운 소식을 받는 메소드
            // TODO Auto-generated method stub
     
            if(obs instanceof Company) {
            
                Company company = (Company)obs;
                
            this.photoUrl = company.getPhotoUrl();
            this.content = company.getContent();
            display();
            }
        }
     
    }
     
    cs



    Subscriber2 클래스



    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
    package javaObserver;
     
    import java.util.Observable;
    import java.util.Observer;
     
    public class Subscriber2 implements Observer, DisplayElement {
     
        private Observable observable;
        private String photoUrl;
        private String content;
     
        public Subscriber2(Observable observable) {
     
            this.observable = observable;
            observable.addObserver(this); // 옵저버로 등록한다.
        }
     
        @Override
        public void display() { // 받은 소식을 화면에 나타내어주는 메소드
            // TODO Auto-generated method stub
            System.out.println("2번째 구독자");
            System.out.println("photo link : " + photoUrl);
            System.out.println("content : " + content);
            System.out.println("\n");
        }
     
        @Override
        public void update(Observable obs, Object args) { // 새로운 소식을 받는 메소드
            // TODO Auto-generated method stub
     
            if (obs instanceof Company) {
                Company company = (Company) obs;
     
                this.photoUrl = company.getPhotoUrl();
                this.content = company.getContent();
                display();
            }
     
        }
    }
    cs




    Messanger 클래스



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package javaObserver;
     
    public class Messenger {
        
        public static void main(String[] args) {
            
            Company company = new Company(); //Subject 클래스를 생성합니다. 
            Subscriber subscriber1 = new Subscriber(company); //Observer 클래스를 생성합니다. 
            Subscriber2 subscriber2 = new Subscriber2(company); //Observer 클래스를 생성합니다. 
     
            
            company.setMessage("photo""이벤트"); //Subject에게 새로운 소식을 전달합니다. 
            
     
        }
     
    }
     
    cs




    저번 예제와 달라진 점은 아래와 같습니다.


    • Subject와 Observer 인터페이스를 각각 java.util 패키지의 Observable 클래스와 Observer 인터페이스가 대체합니다.
    • 옵저버를 등록하는 함수의 이름은 addObserver 입니다.
    • notifyObservers() 메소드 이전에 setChanged() 메소드를 먼저 호출 하여야 합니다.
    • update 함수의 인자로 Company의 레퍼런스를 전달 받아 getter로 값을 가져옵니다.
    저번 예제가 갱신된 모든 정보를 뿌려주는 푸쉬(push) 방식 이었다면, 이번에는 필요한 정보만을 getter로 가져오는 풀(pull) 방식을 사용하였습니다. 

    Observable 클래스와 Observer 인터페이스를 사용하면 푸쉬와 풀 방식을 선택하여 사용할 수 있습니다. 


    자바 객체를 사용 하는 방식은, 편리하게 사용할 수 있지만 Observable 클래스를 상속 받아야 한다는 점은 큰 단점으로 작용합니다. 하나의 클래스는 하나의 부모만을 상속 받을 수 있으니 적절한 상황에서 사용 되어야 합니다.

    또한 setChanged() 메소드는 protected로 선언되어 있습니다. 이는 상속 받은 클래스 내부에서만 사용 되어야 함으로, 상속 보다 구성을 더 사용해야 한다는 디자인 원칙에도 위배 됩니다. 

    자, 이제 위의 프로젝트 파일을 컴파일 하여 실행해 보겠습니다.

    저번 예제는 1번째 구독자 부터 출력되었는데, 지금은 2번째 구독자 부터 출력이 됩니다. 자바 내장 객체를 사용하면 옵저버 들에게 연락이 가는 순서를 보장하지 않습니다. 이 점을 유의해 특정 연락 순서에 의존 하지 않도록 코드를 작성 해야 할 것 입니다.


Designed by Tistory.