7. Observer Pattern ( 옵져버 패턴 C# )

|

[읽기전에]

  UML 다이어그램 : UML클래스 다이어그램 기본상식 http://hongjinhyeon.tistory.com/25 

  포스팅되는 디자인 패턴의 예는 스타크래프트를 기본으로 하였습니다 : 디자인 패턴을 시작하며 http://hongjinhyeon.tistory.com/24



<기본 정의 >


1. Observer Pattern 정의

 

 -한 객체의 상태가 바뀌면 그 책체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로

  일대다(one-to-many) 의존성을 정의한다.

  (Define a one-to-many dependency between objects so that when one object changes state,

   all its dependents are notified and updated automatically.)


 -한 객체의 상태가 변경되면 그 객체에 의존하는 모든 객체에 연락을 한다.

 


2. UML Diagram


 



3. 사용용도 및 장점


 -옵져버 패턴은 MVC 모델에서 View단( 사용자에게 보여주는 단계)에서 많이 나타날수가 있다, 객체의  상태에대한

   참조를 여러군데에서 하고 있을시에 ( 특히 여러개의 창에서 참조가 될시에 ) 사용될 수 있다.


 -객체의 상태를 참조하는 대상에 일관성을 보장한다.


 -객체의 상태가 변경될 시에 참조를 하고있는 대상들은 자동으로 상태가 업데이트가 된다.



4. 소스


001.using System;
002.using System.Collections.Generic;
003.  
004.namespace DoFactory.GangOfFour.Observer.Structural
005.{
006.  /// <summary>
007.  /// MainApp startup class for Structural
008.  /// Observer Design Pattern.
009.  /// </summary>
010.  class MainApp
011.  {
012.    /// <summary>
013.    /// Entry point into console application.
014.    /// </summary>
015.    static void Main()
016.    {
017.      // Configure Observer pattern
018.      ConcreteSubject s = new ConcreteSubject();
019.  
020.      s.Attach(new ConcreteObserver(s, "X"));
021.      s.Attach(new ConcreteObserver(s, "Y"));
022.      s.Attach(new ConcreteObserver(s, "Z"));
023.  
024.      // Change subject and notify observers
025.      s.SubjectState = "ABC";
026.      s.Notify();
027.  
028.      // Wait for user
029.      Console.ReadKey();
030.    }
031.  }
032.  
033.  /// <summary>
034.  /// The 'Subject' abstract class
035.  /// </summary>
036.  abstract class Subject
037.  {
038.    private List<Observer> _observers = new List<Observer>();
039.  
040.    public void Attach(Observer observer)
041.    {
042.      _observers.Add(observer);
043.    }
044.  
045.    public void Detach(Observer observer)
046.    {
047.      _observers.Remove(observer);
048.    }
049.  
050.    public void Notify()
051.    {
052.      foreach (Observer o in _observers)
053.      {
054.        o.Update();
055.      }
056.    }
057.  }
058.  
059.  /// <summary>
060.  /// The 'ConcreteSubject' class
061.  /// </summary>
062.  class ConcreteSubject : Subject
063.  {
064.    private string _subjectState;
065.  
066.    // Gets or sets subject state
067.    public string SubjectState
068.    {
069.      get { return _subjectState; }
070.      set { _subjectState = value; }
071.    }
072.  }
073.  
074.  /// <summary>
075.  /// The 'Observer' abstract class
076.  /// </summary>
077.  abstract class Observer
078.  {
079.    public abstract void Update();
080.  }
081.  
082.  /// <summary>
083.  /// The 'ConcreteObserver' class
084.  /// </summary>
085.  class ConcreteObserver : Observer
086.  {
087.    private string _name;
088.    private string _observerState;
089.    private ConcreteSubject _subject;
090.  
091.    // Constructor
092.    public ConcreteObserver(
093.      ConcreteSubject subject, string name)
094.    {
095.      this._subject = subject;
096.      this._name = name;
097.    }
098.  
099.    public override void Update()
100.    {
101.      _observerState = _subject.SubjectState;
102.      Console.WriteLine("Observer {0}'s new state is {1}",
103.        _name, _observerState);
104.    }
105.  
106.    // Gets or sets subject
107.    public ConcreteSubject Subject
108.    {
109.      get { return _subject; }
110.      set { _subject = value; }
111.    }
112.  }
113.}


 


5. 실행결과


Observer X's new state is ABC

Observer Y's new state is ABC

Observer Z's new state is ABC




< 실제 적용 >


1. UML Diagram






2. 사용용도 및 장점


 -옵저버의 패턴은 스타크래프트에서 필수적인 요소입니다. 사용되는 곳은 여러곳이 있는데 여기서 예를 드는것은 유닛의 상태입니다.

  마린의 체력 상태를 모니터링하는 곳은 메인화면( 케릭터의 에너지바가 칸수로 나옴 ), 상태창(캐릭터의상태가 숫자 표시 및 색으로 표현),

  적의 화면 ( 상대편의 화면 및 케릭터 상태창에서의 체력 표시 )이 있습니다. 


 -아군 마린이 적의 마린의 공격으로 체력이 깍였다면 위의 3군데 모두 동일하게 남은 체력이 보여야 할 것입니다. 이럴때에 옵져버 패턴을

  사용하면 동일한 시점에 동일한 상태를 나타내게 됩니다.


   


3. 소스


001.using System;
002.using System.Collections.Generic;
003.using System.Linq;
004.using System.Text;
005. 
006.namespace Observer
007.{
008.    class Program
009.    {
010.        static void Main(string[] args)
011.        {
012. 
013.            Marine ourMarine = new Marine("아군 마린", 100);
014.            ourMarine.Attach(new MainScreen());
015.            ourMarine.Attach(new StatusScreen());
016.            ourMarine.Attach(new EnemyScreen());
017. 
018.            ourMarine.Health = 60;
019.            ourMarine.Health = 40;
020.           
021.            Console.ReadKey();
022.        }
023.        
024.        abstract class Unit
025.        {
026.            private string name;
027.            private int health;
028.            private List<UnitViewer> unitViewers = new List<UnitViewer>();
029. 
030.            public Unit(string name, int health)
031.            {
032.                this.name = name;
033.                this.health = health;
034.            }
035. 
036.            public void Attach(UnitViewer investor)
037.            {
038.                unitViewers.Add(investor);
039.            }
040. 
041.            public void Detach(UnitViewer investor)
042.            {
043.                unitViewers.Remove(investor);
044.            }
045. 
046.            public void Notify()
047.            {
048.                foreach (UnitViewer unitviewr in unitViewers)
049.                {
050.                    unitviewr.Update(this);
051.                }
052.            }
053. 
054.           
055.            public int Health
056.            {
057.                get { return health; }
058.                set
059.                {
060.                    health = value;
061.                    Notify();
062.                }
063.            }
064.           
065.            public string Name
066.            {
067.                get { return name; }
068.            }
069.        }
070. 
071.        class Marine : Unit
072.        {
073.            public Marine(string name, int health)
074.                : base(name, health)
075.            {
076.            }
077.        }
078. 
079.         
080.        interface UnitViewer
081.        {
082.            void Update(Unit unit);
083.        }
084. 
085. 
086.        class MainScreen : UnitViewer
087.        {
088.            private Unit unit;
089. 
090.            public void Update(Unit _unit)
091.            {
092.                this.unit = _unit;
093.                Console.WriteLine("메인화면 {0} 상태 변경 : 체력 {1}", this.unit.Name, this.unit.Health.ToString());
094.            }
095.             
096.            public Unit Unit
097.            {
098.                get { return unit; }
099.                set { unit = value; }
100.            }
101.        }
102. 
103.        class StatusScreen : UnitViewer
104.        {
105.            private Unit unit;
106. 
107.            public void Update(Unit _unit)
108.            {
109.                this.unit = _unit;
110.                Console.WriteLine("상태창 {0} 상태 변경 : 체력 {1}", this.unit.Name, this.unit.Health.ToString());
111.            }
112. 
113.            public Unit Unit
114.            {
115.                get { return unit; }
116.                set { unit = value; }
117.            }
118.        }
119. 
120.        class EnemyScreen : UnitViewer
121.        {
122.            private Unit unit;
123. 
124.            public void Update(Unit _unit)
125.            {
126.                this.unit = _unit;
127.                Console.WriteLine("적 상태창 {0} 상태 변경 : 체력 {1}", this.unit.Name, this.unit.Health.ToString());
128.            }
129. 
130.            public Unit Unit
131.            {
132.                get { return unit; }
133.                set { unit = value; }
134.            }
135.        }
136.    }
137.}

-UnitViewer를 인터페이스로 선언한 것은 참조되는 클래스들이 연관성이 많이 없을 때 유용하다. 

 굳이 UnitViewer를 상속해야하는 제한에서 벗어나서 마린의 객체를 참조하고자 하는 클래스들은 옵저버 인터페이스를 구현만 하면

 상속받은 객체가 아니라도 마린의 상태를 볼 수 있게된다. (즉 더 유연한 구현을 할수가 있게된다.)



4. 실행결과





5. 피드백


  -옵져버 패턴은 현업에서도 자주 사용될 수있는 패턴인듯하다. 특히 사용자에게 자료를 여러군데에서 보여줄때, 자료가 수정되면

   열려진 창에서 모두데이터가 수정되게 하는 부분에서 유용할 듯 하다.




[참조]

 -http://www.dofactory.com

 -Head First Design Patterns

And