الگوی Observer

الگوی Observer(مشاهده گر-مشاهده کننده) در دسته الگوهای رفتاری قرار دارد. یک وابستگی یک به چند را بین اشیاء تعریف می کند تا زمانی که یک شی تغییر وضعیت دهد، به تمام وابستگان آن به طور خودکار اطلاع داده می شوند و به روز می شوند. به عبارت دیگر بین یک شیء و تعدادی شی دیگر رابطه ی یک به چند تعریف می کند به گونه ای که وقتی شیء نخست تغییر می کند وقوع تغییر را به اشیاء مرتبط با خود اعلام می کند تا آنها به دنبال آن کاری را به تناسب انجام بدهد. UML class diagram شركت كنندگان کلاس ها و اشیاء شرکت کننده در این الگو عبارتند از:موضوع Subject (موجودی در مثال دنیای واقعی Stock): ناظران(observers) خود را می شناسد. هر تعداد اشیاء مشاهده کننده (Observer) ممکن است یک موضوع (Subject) را مشاهده کنند. فراهم می کند واسطی برای اتصال(attaching) و جدا کردن(detaching) اشیاء Observer.به عبارت دیگر کلاس مجردی است که ساختار کلی Publisher را تعریف می کند. این کلاس از طریق متد های Attach() و Detach() به ترتیب قابلیت پذیرفتن مشترکین یا لغو اشتراک را فراهم می کند.نگهداری مشترکین بر اساس کلاس مجرد Observer انجام میشود. از این رو Subject از هویت واقعی مشترکین آگاه نیست (آنها خود را در قالب Observer به او معرفی می کنند و از نظر او همه آنها آب Observer هستند). موضوع سفت ConcreteSubject (شرکت IBM):انبار وضعیت مورد علاقه به ConcreteObserver هستند. هنگامی که وضعیت آن تغییر می کند، اطلاع رسانی(notification) به ناظران(observers) خود ارسال می کند.به عبارت دیگر نمونه عملی از پیاده سازی کلاس انتزاعی Subject است. مشاهده کننده Observer (سرمایه گذار IInvestor): تعریف یک واسط به روز رسانی برای اشیائی است که باید از تغییرات در یک موضوع (Subject) مطلع شوند. به عبارت دیگر کلاس مجردی است که ساختار کلی مشترکین را تعریف میکند. این واسط متدی به اسم Update() تعریف می کندو Subject بر اساس آن هنگام آگاه سازی، این متد را برای مشترکین خود فراخوانی می کند. مشاهده کننده سفت ConcreteObserver (سرمایه گذار Investor):یک ارجاع(reference) به یک شی ConcreteSubject می دهد.انبار وضعیت که باید با موضوع (Subject)هماهنگ باشند.واسط به روزرسانی Observer را پیاده سازی می کند تا وضعیت را با موضوع(Subject) سازگار سازد.به عبارت دیگر کلاسی است که از Observer مشتق شده و آن را پیاده سازی کرده است. ساختار کلی کدالگوی Observer را نشان می دهد که در آن اشیاء ثبت شده با تغییر وضعیت تغییر می کنند و با آنها به روز می شود. 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; } } } } OutputObserver X 's new state is ABCObserver Y 's new state is ABCObserver Z 's new state is ABCیا abstract class Subject { protected List<Observer> observers = new List<Observer>(); public virtual void Attach(Observer observer) { observers.Add(observer); } public virtual void Detach(Observer observer) { observers.Remove(observer); } public virtual void Notify() { foreach (Observer o in observers) { o.Update(); } } } class ConcreteSubject: Subject { protected int subjectState; public virtual int GetState() { return subjectState; } public virtual void SetState(int value) { subjectState = value; Notify(); } } abstract class Observer { public abstract void Update(); } class ConcreteObserver: Observer { private ConcreteSubject subject; private string name; private int observerState; private int factor; public ConcreteObserver(ConcreteSubject subject, string name, int factor) { this.subject = subject; this.name = name; this.factor = factor; } public override void Update() { observerState = subject.GetState() * factor; Console.WriteLine("{0}'s new state is {1}", name, observerState); } } class Test { static void Main() { var s = new ConcreteSubject(); s.Attach(new ConcreteObserver(s, "Observer-X", 10)); s.Attach(new ConcreteObserver(s, "Observer-Y", 20)); s.Attach(new ConcreteObserver(s, "Observer-Z", 30)); s.SetState(12); Console.ReadKey(); } } /* OUTPUT:Observer-X's new state is 120Observer-Y's new state is 240Observer-Z's new state is 360*/مثال دنیای واقعیالگوی Observer را نشان می دهد که در آن سرمایه گذاران ثبت شده هر زمانی که ارزش سهام تغییر می کنند، مطلع می شوند. using System; using System.Collections.Generic; namespace DoFactory.GangOfFour.Observer.RealWorld { /// <summary> /// MainApp startup class for Real-World /// Observer Design Pattern. /// </summary> class MainApp { /// <summary> /// Entry point into console application. /// </summary> static void Main () { // Create IBM stock and attach investors IBM ibm = new IBM ("IBM", 120.00); ibm.Attach (new Investor ("Sorros")); ibm.Attach (new Investor ("Berkshire")); // Fluctuating prices will notify investors ibm.Price = 120.10; ibm.Price = 121.00; ibm.Price = 120.50; ibm.Price = 120.75; // Wait for user Console.ReadKey (); } } /// <summary> /// The 'Subject' abstract class /// </summary> abstract class Stock { private string _symbol; private double _price; private List<IInvestor> _investors = new List<IInvestor> (); // Constructor public Stock (string symbol, double price) { this._symbol = symbol; this._price = price; } public void Attach (IInvestor investor) { _investors.Add (investor); } public void Detach (IInvestor investor) { _investors.Remove (investor); } public void Notify () { foreach (IInvestor investor in _investors) { investor.Update (this); } Console.WriteLine (""); } // Gets or sets the price public double Price { get { return _price; } set { if (_price != value) { _price = value; Notify (); } } } // Gets the symbol public string Symbol { get { return _symbol; } } } /// <summary> /// The 'ConcreteSubject' class /// </summary> class IBM : Stock { // Constructor public IBM (string symbol, double price) : base (symbol, price) { } } /// <summary> /// The 'Observer' interface /// </summary> interface IInvestor { void Update (Stock stock); } /// <summary> /// The 'ConcreteObserver' class /// </summary> class Investor : IInvestor { private string _name; private Stock _stock; // Constructor public Investor (string name) { this._name = name; } public void Update (Stock stock) { Console.WriteLine ("Notified {0} of {1}'s " + "change to {2:C}", _name, stock.Symbol, stock.Price); } // Gets or sets the stock public Stock Stock { get { return _stock; } set { _stock = value; } } } } OutputNotified Sorros of IBM's change to $120.10Notified Berkshire of IBM's change to $120.10Notified Sorros of IBM's change to $121.00Notified Berkshire of IBM's change to $121.00Notified Sorros of IBM's change to $120.50Notified Berkshire of IBM's change to $120.50Notified Sorros of IBM's change to $120.75Notified Berkshire of IBM's change to $120.75منبع