처음엔 옵저버 패턴과 같은 줄 알았는데, 아니었다.(비슷함)
다음은 옵저버 패턴의 UML이다.
위의 구조를 보면, 옵저버 패턴에서는 notifyObservers() 함수 내에서 루프를 돌며(물론 모든 옵저버 패턴이 무조건 루프 방식은 아닐 수도 있지만)
모든 옵저버들의 내용을 갱신한다.
파생 옵저버들(이런저런 구독자들이라 할 수 있음)은 Observer 클래스를 부모로 가지고 있어야 하며, 꼭 Subject 클래스 내부에서 어떤 식으로든 참조할 수 있어야 한다.
하지만 C# Event 발행자/구독자 방식은 조금 다르다.
발행자는 구독자에게 자신의 이벤트 처리 방식을 넣어 준다.
즉, 구독자는 그저 자신이 어떤 행동을 했을 때 원하는 결과가 나오도록 해 주는 발행자를 선택하면 되는 것이다.
그래서 발행자는 구독자에 대한 리스트를 가질 필요가 없다.
하지만 구독자 리스트를 가지고 뭔가를 하는 순간부터 일반적인 옵저버 패턴과 같아진다.
마우스.좌클릭 += 발행자1 제공함수
마우스.우클릭 += 발행자2 제공함수
마우스 휠 += 발행자3 제공함수
.
.
.
.
흠... 엄밀히 말하면 모양 자체는 옵저버 패턴과 같은 방식인 것 같다.
결국 목적은 옵저버 패턴과 같으니까(발행/구독/취소)
일반적이지 않은 옵저버 패턴..?
아래는 MSDN에서 그에 대한 내용이다. (이유는 모르겠지만 List를 가짐)
https://msdn.microsoft.com/ko-kr/library/hy3sefw3(v=vs.80).aspx
namespace BaseClassEvents { using System; using System.Collections.Generic; // Special EventArgs class to hold info about Shapes. public class ShapeEventArgs : EventArgs { private double newArea; public ShapeEventArgs(double d) { newArea = d; } public double NewArea { get { return newArea; } } } // Base class event publisher public abstract class Shape { protected double area; public double Area { get { return area; } set { area = value; } } // The event. Note that by using the generic EventHandler<T> event type // we do not need to declare a separate delegate type. public event EventHandler<ShapeEventArgs> ShapeChanged; public abstract void Draw(); //The event-invoking method that derived classes can override. protected virtual void OnShapeChanged(ShapeEventArgs e) { // Make a temporary copy of the event to avoid possibility of // a race condition if the last subscriber unsubscribes // immediately after the null check and before the event is raised. EventHandler<ShapeEventArgs> handler = ShapeChanged; if (handler != null) { handler(this, e); } } } public class Circle : Shape { private double radius; public Circle(double d) { radius = d; area = 3.14 * radius; } public void Update(double d) { radius = d; area = 3.14 * radius; OnShapeChanged(new ShapeEventArgs(area)); } protected override void OnShapeChanged(ShapeEventArgs e) { // Do any circle-specific processing here. // Call the base class event invocation method. base.OnShapeChanged(e); } public override void Draw() { Console.WriteLine("Drawing a circle"); } } public class Rectangle : Shape { private double length; private double width; public Rectangle(double length, double width) { this.length = length; this.width = width; area = length * width; } public void Update(double length, double width) { this.length = length; this.width = width; area = length * width; OnShapeChanged(new ShapeEventArgs(area)); } protected override void OnShapeChanged(ShapeEventArgs e) { // Do any rectangle-specific processing here. // Call the base class event invocation method. base.OnShapeChanged(e); } public override void Draw() { Console.WriteLine("Drawing a rectangle"); } } // Represents the surface on which the shapes are drawn // Subscribes to shape events so that it knows // when to redraw a shape. public class ShapeContainer { List<Shape> _list; public ShapeContainer() { _list = new List<Shape>(); } public void AddShape(Shape s) { _list.Add(s); // Subscribe to the base class event. s.ShapeChanged += HandleShapeChanged; } // ...Other methods to draw, resize, etc. private void HandleShapeChanged(object sender, ShapeEventArgs e) { Shape s = (Shape)sender; // Diagnostic message for demonstration purposes. Console.WriteLine("Received event. Shape area is now {0}", e.NewArea); // Redraw the shape here. s.Draw(); } } class Test { static void Main(string[] args) { //Create the event publishers and subscriber Circle c1 = new Circle(54); Rectangle r1 = new Rectangle(12, 9); ShapeContainer sc = new ShapeContainer(); // Add the shapes to the container. sc.AddShape(c1); sc.AddShape(r1); // Cause some events to be raised. c1.Update(57); r1.Update(7, 7); // Keep the console window open. Console.WriteLine(); Console.WriteLine("Press Enter to exit"); Console.ReadLine(); } } }
출력
Received event. Shape area is now 178.98 Drawing a circle Received event. Shape area is now 49 Drawing a rectangle
반응형
'C#' 카테고리의 다른 글
[C#] IEnumerator, IEnumerable과 yield에 대해. (1) | 2017.02.08 |
---|---|
[C#] Float형을 byte형으로 변환하기 (0) | 2016.12.14 |
[C#] Using 키워드의 용도 (0) | 2016.12.14 |
[C#] DataTable.Copy() 와 DataTable.Clone()의 차이 (0) | 2016.09.19 |
[C#] WeakReference (약한 참조) (0) | 2016.08.21 |
[스크랩][C#] NULL 처리된 객체가 Garbage collector에 의해 수집되지 않는 현상 (0) | 2016.08.21 |
[C#][Unity3D] List<T> Remove 사용 주의사항 (1) | 2013.03.07 |
[C#] Json 파서 구현 시 엑셀 파일 로딩에 대해 (0) | 2013.02.07 |
C# char, sbyte (0) | 2013.01.17 |
C# Delegate 그리고 Event (1) | 2013.01.08 |