الگوی Observer یکی از الگوهای رفتاری می باشد. همان طور که از نام
این الگو مشخص است، در این الگو برخی اشیا عملکرد شی خاصی را مشاهده یا رصد می کنند
و هر گونه تغییر در وضعیت شی مورد نظر بر روی اشیا ناظر تاثیر گذار خواهد بود. به
این ترتیب وابستگی بین اشیا ناظر و شی هدف وجود دارد و نیاز است که همه اشیا ناظر
از هر گونه تغییر حالت شی هدف اطلاع پیدا کنند. یکی از راه حل هایی که برای این
سناریو وجود دارد استفاده از حلقه For می باشد. اما این راه حل هزینه بر بوده
و کارایی را کاهش می دهد. لذا در چنین مواردی استفاده از الگوی Observer بسیار کمک
کننده خواهد بود. این الگوی طراحی بسیار کاربردی می باشد و از ارتباط Broadcast
پشتیبانی می کند. Observer یکی از مهمترین بخش های معماری MVC محسوب می شود.
کاربردهای الگوی طراحی Observer
الگوی Observer را می توان در موارد زیر استفاده نمود:
روش های ارتباط Subject و Observer
برای ارتباط بین Subject و Observer دو روش وجود دارد:
- روش Push: در این روش Subject همه تغییرات انجام شده را برای کلیه
Observerها به صورت کامل ارسال می کند و هر کدام از اشیا آدر صورتی که نیاز به
آپدیت یا تغییری داشته باشند اطلاعات مورد نیاز خود را استخراج می نمایند.
- روش Pull: در این روش Subject به همه Observerها اطلاع می دهد که تغییری
انجام شده است و هر کدام از Observerها که نیاز به آپدیت یا تغییر داشته باشند
پیامی برای ارسال کامل اطلاعات را به Subject می فرستند و پس از آن Subject به
اشیایی که درخواست اطلاعات نموده اند تغییرات درخواست شده را ارسال می نماید.
نمودار کلاس دیاگرام الگوی طراحی Observer
همان طور که در دیاگرام مشخص است نمودار کلاس الگوی Observer شامل کلاس های زیر است:
- کلاس Subject: کلاس هدف می باشد که
تغییرات آن برای سایر اشیا اهمیت دارد و در واقع ممکن است یک یا چند کلاس به
عنوان مشاهده کننده تغییرات آن را دنبال کنند. در این کلاس توابعی برای افزودن
یا حذف مشاهده کننده ها پیاده سازی می شود.
- کلاس ConcreteSubject: وضعیت مشاهده کننده
ها در این کلاس نگهداری می شود و در زمان هایی که تغییراتی در هدف رخ می
دهد، پیام های لازم را به مشاهده کننده ها ارسال می کند.
- کلاس Observer: کلاس مشاهده کننده که
تغییرات Subject برای آن مهم است و متد Update برای اطلاع رسانی به اشیا را
پیاده سازی می نماید.
- کلاس ConcreteObserver: یک مرجع برای اشیا
ConcreteSubject ایجاد می کند و حالتی که توسط Subject باید پایدار بماند را ذخیره می
کند. همچنین برای آپدیت Subjectها متدی را پیاده سازی می کند.
در ادامه، پیاده سازی این الگو را در یک مثال توضیح می دهیم:
همان طور که می دانیم، در بورس شرکت ها و سهامداران مختلفی وجود دارند
و هر کدام از سهامداران ممکن است سهام یک یا چندین شرکت را داشته باشند. بنابراین
تغییر در قیمت هر کدام از شرکت ها برای شهامداران آن شرکت مهم بوده و نیاز
دارند که از تغییرات شرکت خود اطلاع داشته باشند. در صورتی که بخواهیم اطلاع رسانی
برای هر سهامدار در هر شرکت را با استفاده از عملگرهای کنترلی حلقه ها پیاده سازی
نماییم ممکن است کاری پر هزینه باشد اما این سناریو به راحتی می تواند با استفاده
از الگوی طراحی Observer پیاده سازی شود.
کلاس Stock که در واقع کلاس هدف می باشد و پیاده سازی آن به صورت زیر
انجام می شود:
abstract class Stock
{
private string _symbol;
private double _price;
private List _investors = new List();
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("");
}
public double Price
{
get { return _price; }
set
{
if (_price != value)
{
_price = value;
Notify();
}
}
}
public string Symbol
{
get { return _symbol; }
}
}
}
برای هر کدام از شرکت ها مثلا IBM که نقش ConcreteSubject را داشته می
توان کلاسی به صورت زیر پیاده سازی نمود که از کلاس Subject ارث بری می کند:
class IBM : Stock
{
public IBM(string symbol, double price)
: base(symbol, price)
{
}
}
Interfaceی برای سهامداران که در واقع به عنوان مشاهده کننده در نظر
گرفته می شود تا تغییرات را پیاده سازی نماید و به صورت زیر می باشد:
interface IInvestor
{
void Update(Stock stock);
}
کلاس سهامداران که نقش ConcreteObserver را داشته و از کلاس IInvestor ارث بری نموده است.
این کلاس به صورت زیر
پیاده سازی می شود:
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; }
}
}
بنابراین در برنامه می توان پس از تعریف شرکت های مختلف و اختصاص
سهامداران مختلف به آن ها، با هر بار تغییر قیمت، بدون درگیر شدن برای پیاده سازی
اطلاع رسانی، به سهامداران اطلاع
رسانی نمود:
IBM ibm = new IBM("IBM", 120.00);
ibm.Attach(new Investor("Sorros"));
ibm.Attach(new Investor("Berkshire"));
ibm.Price = 120.10;
ibm.Price = 121.00;
ibm.Price = 120.50;
ibm.Price = 120.75;