[읽기전에]
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. 소스
using System; using System.Collections.Generic; namespace DoFactory.GangOfFour.Observer.Structural { /// <summary> /// MainApp startup class for Structural /// Observer Design Pattern. /// </summary> class MainApp { /// <summary> /// Entry point into console application. /// </summary> static void Main() { // Configure Observer pattern ConcreteSubject s = new ConcreteSubject(); s.Attach(new ConcreteObserver(s, "X")); s.Attach(new ConcreteObserver(s, "Y")); s.Attach(new ConcreteObserver(s, "Z")); // Change subject and notify observers s.SubjectState = "ABC"; s.Notify(); // Wait for user Console.ReadKey(); } } /// <summary> /// The 'Subject' abstract class /// </summary> abstract class Subject { private List<Observer> _observers = new List<Observer>(); public void Attach(Observer observer) { _observers.Add(observer); } public void Detach(Observer observer) { _observers.Remove(observer); } public void Notify() { foreach (Observer o in _observers) { o.Update(); } } } /// <summary> /// The 'ConcreteSubject' class /// </summary> class ConcreteSubject : Subject { private string _subjectState; // Gets or sets subject state public string SubjectState { get { return _subjectState; } set { _subjectState = value; } } } /// <summary> /// The 'Observer' abstract class /// </summary> abstract class Observer { public abstract void Update(); } /// <summary> /// The 'ConcreteObserver' class /// </summary> class ConcreteObserver : Observer { private string _name; private string _observerState; private ConcreteSubject _subject; // Constructor public ConcreteObserver( ConcreteSubject subject, string name) { this._subject = subject; this._name = name; } public override void Update() { _observerState = _subject.SubjectState; Console.WriteLine("Observer {0}'s new state is {1}", _name, _observerState); } // Gets or sets subject public ConcreteSubject Subject { get { return _subject; } set { _subject = value; } } } }
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. 소스
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Observer { class Program { static void Main(string[] args) { Marine ourMarine = new Marine("아군 마린", 100); ourMarine.Attach(new MainScreen()); ourMarine.Attach(new StatusScreen()); ourMarine.Attach(new EnemyScreen()); ourMarine.Health = 60; ourMarine.Health = 40; Console.ReadKey(); } abstract class Unit { private string name; private int health; private List<UnitViewer> unitViewers = new List<UnitViewer>(); public Unit(string name, int health) { this.name = name; this.health = health; } public void Attach(UnitViewer investor) { unitViewers.Add(investor); } public void Detach(UnitViewer investor) { unitViewers.Remove(investor); } public void Notify() { foreach (UnitViewer unitviewr in unitViewers) { unitviewr.Update(this); } } public int Health { get { return health; } set { health = value; Notify(); } } public string Name { get { return name; } } } class Marine : Unit { public Marine(string name, int health) : base(name, health) { } } interface UnitViewer { void Update(Unit unit); } class MainScreen : UnitViewer { private Unit unit; public void Update(Unit _unit) { this.unit = _unit; Console.WriteLine("메인화면 {0} 상태 변경 : 체력 {1}", this.unit.Name, this.unit.Health.ToString()); } public Unit Unit { get { return unit; } set { unit = value; } } } class StatusScreen : UnitViewer { private Unit unit; public void Update(Unit _unit) { this.unit = _unit; Console.WriteLine("상태창 {0} 상태 변경 : 체력 {1}", this.unit.Name, this.unit.Health.ToString()); } public Unit Unit { get { return unit; } set { unit = value; } } } class EnemyScreen : UnitViewer { private Unit unit; public void Update(Unit _unit) { this.unit = _unit; Console.WriteLine("적 상태창 {0} 상태 변경 : 체력 {1}", this.unit.Name, this.unit.Health.ToString()); } public Unit Unit { get { return unit; } set { unit = value; } } } } }
-UnitViewer를 인터페이스로 선언한 것은 참조되는 클래스들이 연관성이 많이 없을 때 유용하다.
굳이 UnitViewer를 상속해야하는 제한에서 벗어나서 마린의 객체를 참조하고자 하는 클래스들은 옵저버 인터페이스를 구현만 하면
상속받은 객체가 아니라도 마린의 상태를 볼 수 있게된다. (즉 더 유연한 구현을 할수가 있게된다.)
4. 실행결과
5. 피드백
-옵져버 패턴은 현업에서도 자주 사용될 수있는 패턴인듯하다. 특히 사용자에게 자료를 여러군데에서 보여줄때, 자료가 수정되면
열려진 창에서 모두데이터가 수정되게 하는 부분에서 유용할 듯 하다.
[참조]
-http://www.dofactory.com
-Head First Design Patterns
'프로젝트 설계 > 디자인 패턴' 카테고리의 다른 글
9. Command Pattern ( 커맨드 패턴 C# ) (1) | 2012.09.11 |
---|---|
8. Strategy Pattern ( 스트래티지 / 전략 패턴 C# ) (9) | 2012.09.06 |
6. Facade Pattern (퍼사드 패턴 C# ) (2) | 2012.09.04 |
5. Decorator Pattern ( 데코레이터 / 장식자 패턴 C# ) (1) | 2012.08.29 |
4. Adapter Pattern ( 어댑터 / 적응자 패턴 C# ) (2) | 2012.08.28 |