ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 닷넷 3.5를 이용한 디자인 패턴의 구현 (5)
    DesignPatterns 2009. 8. 6. 22:41
    반응형


    닷넷 3.5를 이용한 디자인 패턴의 구현 (4) 에서본
     






    Observer 패턴을 다시한번 더 본다



    새롭고 세련된 윈도우 형식의 세상에서 우리는 종종 동시에 한 가지 이상의 형식으로 데이터를 표현하고자 하고, 그 데이터에 발생한 모든 변경사항도 한 번에 나타내기를 원한다. 예를 들어, 증권시세의 변화를 그래프와도표 또는 목록 상자로 표시할 수 있다. 시세가 변할 떄마다 우리가 수동으로 별다른 조치를 취하지 않고도 한 번에 모든 수치가 변하기를 바라기도 한다.

    이러한 종류의 행위가 이루어지길 바라는 것은 우리가 그동안 엑셀과 같은 수 많은 윈도우 애플리케이션에서 이런 일들이 이루어지고 있는 것을 보았기 때문이다. 윈도우에는 이러한 행위를 허용해주는 상속성이 없으며, 여러분도 알다시피 C/C++로 윈도우에 직접 프로그램을 작성하는 것도 매우 복잡하다. 하지만 C#에서는 Observer 패턴을 이용하여 쉽게 원하는 방식으로 프로그램이 작동하도록 만들 수 있다.
    Observer 패턴은 데이터를 가지고 있는 객체가 데이터를 디스플레이하는 객체와는 분리되어 있으며, 디스플레이 객체는 데이터상에서일어나는 변화를 관찰(Observer)한다고 가정하고 있다
    아래 그림을 참고하면 이 쉽게 이해가 될것이다


                                                 데이터는 목록과 그래픽 모드로 표시된다


    Observer 패턴을 구현할 때 우리는 주로 데이터를 Subject로, 각각의 디스플레이를 Observer로 지정한다.
     
    각각의 Observer는 Subject는 내의 공개 메서드를 호출하여 데이터 내에 자신의 관심을 등록한다. 그리고나서, 각 Observer는 데이터가 변하면 Subject가 호출하게 되는, 알려진 인터페이스를 갖는다. 이러한 인터페이스는 다음과 같이 정의 할 수 있다.

    public interface Observer
    {
         void sendNotify(string message)
          
         public inteface Subject 
         {
             void registerInterest(Observer obs);
         }
    }

    추상적인 인터페이스를 정의함으로써 따르는 이점은

    첫째, 여러분이 원하는 클래스 객체가 이러한 인터페이스를 구현하는 한. 어떠한 종류의 클래스 객체라도 작성할 수 있다.
    둘째, 이들이 하는 일이 무엇이든지 여러분은 이 객체들을 Subjec 타입이나 Observer로 선언할 수 있다는 것이다


    색 변화 관찰(예제)

    이렇게 강력한 개념을 어떻게 사용할 수 있는지를 설명할 간단한 프로그램을 작성해보자.

    이 프로그램은 Red , Green , Blue라는 이름의 옵션 단추를 가지고 있다.



    이제 우리가 가진 메인 Form 클래스는 Subject 인터페이스르 구현한다.

    이 말은 이 클래스가 클래스 내에 있는 데이터에 대한 관심을 등록하는 데 필요한 공개 메서드를 제공해야 한다는 의미이다.

    이 메서드는 registerInterest 메서드로, 단순히 ArrayList에 Observer 객체를 추가한다

    public void registerInterest(Observer obs)
    {
         observers.Add(obs);
    }


    이제는 두 가지 Observer를 생성한다.
    하나는 색(그리고 이름)을 표시하고,
    다른 하나는 목록 상자에 현재의 색을 추가한다.

    각각의 Observer는 실제로 Observer 인터페이스를 구현하는 윈도우 형식이다. 이러한 형식의 인터페이스를 생성할 때, 우리는 기초 또는 시작 폼을 인수로써 두 Observer에게 전달한다. 이 시작 폼은 실질적으로는 Subject이기 때문에 Observer는 이벤트 내에 자신들의 관심을 등록할 수 있다. 따라서 메인 폼이 초기 인스턴스를 생성하고 이 인스턴스로 자체 참조를 전달한다

    ListObs lobs = new ListObs(this);
    lobs.Show();
    ColObserver colObs = new ColObserver(this);
    colObs.Show();

    그리고 나서, ListObs 윈도우를 생성하면, 메인 프로그램 내에 있는 데이터에 우리의 관심을 등록한다.

    public ListObs(Subject subj)
    {
        InitializeComponent();
        init(subj);
    }

    public void init(Subject subj)
    {
        subj.registerInterest(this);
    }

    메인 Subject 프로그램으로부터 sendNotify 메시지를 받으면 목록에 색 이름을 추가하기만 하면된다.


    public void senNotify(string message)
    {
       lsColors.Items.Add(message);
    }

    색 윈도우 Observer이므로 그림 상자의 배경색을 바꿔야 하고, 브러쉬를 사용하여 색 이름을 칠해야 한다. sendNotify 이벤트 내에서 그림 상자의 배경색을 바꾸고 paint 이벤트 내에서 텍스트를 바꾼다는 것에 주목하자. 다음은 클래스 전체를 나타낸 것이다.


        /// <summary>
        /// Summary description for ColObserver.
        /// </summary>
        public class ColObserver : System.Windows.Forms.Form, Observer
        {
            private System.ComponentModel.Container components = null;
            private Brush bBrush;
            private System.Windows.Forms.PictureBox pic;
            private Font fnt;
            private Hashtable colors;
            private string colName;
            //-----
            public ColObserver(Subject subj)
            {
                InitializeComponent();
                init(subj);
            }
            //-----
            private void init(Subject subj)
            {
                subj.registerInterest(this);
                fnt = new Font("arial", 18, FontStyle.Bold);
                bBrush = new SolidBrush(Color.Black);
                pic.Paint += new PaintEventHandler(paintHandler);
                colors = new Hashtable();
                colors.Add("red", Color.Red);
                colors.Add("blue", Color.Blue);
                colors.Add("green", Color.Green);
                colName = "";
            }
            //-----
            public void sendNotify(string message)
            {
                colName = message;
                message = message.ToLower();
                Color col = (Color)colors[message];
                pic.BackColor = col;
            }
            //-----
            private void paintHandler(object sender, PaintEventArgs e)
            {
                Graphics g = e.Graphics;
                g.DrawString(colName, fnt, bBrush, 20, 40);
            }

     sendNotify 이벤트가 색 이름을 나타내는 문자열을 부여받고,
    우리가 해시 테이블을 이용하여 이 문자열을 실제 Color객체로 전환한 것에 주목하자.
    그러는 동안 메인 프로그램에서는 매번 누군가가 옵션 단추 중 하나를 클릭할 때 마다 단순히 Observer의 Collection내에 객체를 조사하여 이러한 변경사항에 대한 관심을 등록한 각 Observer의 SendNotify 메서드를 호출한다.

            private void CheckedChanged(object sender, EventArgs e)
            {
                RadioButton but = (RadioButton)sender;
                for (int i = 0; i < observers.Count; i++)
                {
                    Observer obs = (Observer)observers[i];
                    obs.sendNotify(but.Text);
                }
            }

    ColorForm observer의 경우 , sendNotify메서드는 PictureBox폼의 배경색과 텍스트 문자열을 변경한다 . 하지만 ListForm Observer의 경우에는 목록 상자에 새로운 색의 이름을 추가하기만 하면 된다




     데이터 컨트롤 패널은 색 패널과 목록 상자에 동시에 나타나는 데이터를 생성한다.
    이 데이터는 Observer 패턴의 후보자가 된다


    Observer패턴의 Observer 인터페이스와 Subject 인터페이스 구현



    미디어를 위한 메시지

    Subject가 Observer에게 어떤 종류의 통보를 보내야 하는가? 위와 같이 제한된 예제에서 통보 메시지는 색 자체를 나타내는 문자열을 말한다. 옵션 단추 중 하나를 클릭하면 그 단추에 대한 캡션이 나타나고, 이 캡션은 Observer에게 전달된다. 물론 이것은 모든 Observer가 문자열 표시를 처리할 수 있다는 전제에서 그렇다는 것이다 . 좀더 현실적인 상황에서는 그렇지 않을 수도 있다. 특히 Observer가 다른 데이터 객체를 관찰하는데 사용된다면 더욱 그럴 것이다. 여기서는 간단한 두 가지 데이터 변환을 실행한다.

    1. 옵션 단추로부터 레이블을 얻고 이 레이블을 Observer로 보낸다.
    2. 그 레이블을  ColObserver내의 실제 색으로 변환한다.

    좀더 복잡한 시스템에서는 조금 다른 종류의 상세한 데이터를 요청하는 Observer가 있을 수 있다.
    각 Observer가 올바른 데이터 타입으로 메시지를 변환하도록 하는 대신 우리는 이러한 변환을 수행하기 위해 중간 Adapter클래스를 사용할 수 있다

    Observer가 처리해야 하는 또 다른 문제는 중앙 Subject 클래스의 데이터가 여러 가지방식으로 변할 수 있는 경우이다. 그러면 데이터 목록에서 포인트를 삭제하고, 값을 변경하거나, 우리가 현재 보고 있는 데이터의 크기를 변경할 수 있다. 그런 경우에는 모든 Observer에게 각각 다른 변경 메시지를 보내거나, 단일 메시지를 보내고 Observer가 어떤 종류의 변경사항이 발생했는지를 묻도록 해야한다.

    Observer 패턴의 결론

    Observer는 subject로의 추상적인 결합성을 조장한다. subject는 Observer의 세부 사항에 대해 전혀 모르고 있다. 하지만 이러한 특징으로 인해 데이터에 점직적으고 연속적인 변화가 일어나게 되면 Observer에서 연속적이고 반복적으로 갱신해야 하는 잠재적인 단점도 있다. 갱신에 대한 대가가 너무 크다면, 변경 관리와 같은 방식을 도입하여 Observer가 너무 조기에 , 또는 너무 자주 통보를 받지 않도록 해야하는 경우도 있다.

    클라이언트가 데이터를 수정하면 여러분은 어떤 객체가 다른 Observer에게 그러한 변경사항에 대해 통보를 할 것인가를 결정해야 한다. 만약 변경사항이 발생했을 떄 subject가 모든 Observer에게 통보한다면 각 클라이언트는 통보해야 하는 것을 기억할 책임이 없다. 하지만 이로 인해 다수의 연속적인 갱신이 발생되는 결과가 생길 수도 있다. 만약 클라이언트가 다른 클라이언트에게 언제 통보해야 하는지를 subject에 알려주면, 이와 같은 단계식 통보는 피할 수 있지만 클라이언트가 언제 통보해야 할지 subject에게 알려야 하는 책임은 남는다. 만약 클라이언트가 이를 잊어버린다면 프로그램은 제대로 작동하지 않게 된다

    마지막으로 여러분은 변경사항의 타입과 범위에 따라 Observer가 받는 여러 가지 갱신 방법을 정의하여 전달하고자 하는 통보의 종류를 명세할 수 있다. 어떤 경우에는 클라이언트가 이러한 몇 가지 통보를 무시할 수도 있을 것이다.


    아래는 이장의 전체 예제



    - 참고  예제로 배우는 C# 디자인 패턴 / 제임스 W.쿠퍼
    반응형

    댓글

Designed by Tistory.