حوزه: Object
هدف: Behavioral
نقش الگو
حالتی را تصور کنید که چند شی در برنامه وجود دارد که تغییر حالات یک شی دیگر را دنبال میکنند و با توجه به هر تغییر عملی را انجام میدهند. شاید اولین راه حلی که در این حالت یافت شود، این است که اشیا در یک حلقه، شی مورد نظر را بررسی کنند تا اگر تغییری به وجود آمده بود با توجه به آن کاری انجام دهند. اما این بدترین راه حل است و باعث افت کارایی و همچنین طراحی بد برنامه خواهد شد. برای حل این مسئله الگوی Observer معرفی میشود.
الگوی Observer یک ارتباط بین اشیا ایجاد میکند به طوری که وقتی یکی از اشیا تغییر حالت میدهد همه اشیا مرتبط با آن از این تغییر حالت اطلاع پیدا میکنند. معمولاَ در یک مجموعه از اشیا مرتبط این چنینی، یک منتشر کننده(publisher) و جود دارد و چندین شی وجود دارد که از تغییر حالت شی منتشر کننده آگاه می شوند.
تشریح
در دنیای کامپیوتر امروزی از این الگو بسیار استفاده شده است. به عنوان مثال حالتی را تصور کنید که در یک سایت آمار فروش به صورت یک نمودار نمایش داده میشود. وقتی که یک عمل فروش انجام میشود این نمودار باید با توجه به مقدار حجم فروشها، خود را بروزرسانی کند. حال شاید بتوان گفت که این کار را میتوان بدون استفاده از الگویی پیاده سازی کرد ولی وقتی نیاز به الگوی Observer احساس میشود که در زمان اجرا بخواهیم مجموعه اشیائی را که تغییر شی منتشر کننده را دنبال میکنند را تغییر بدهیم، یعنی چند شی به این مجموعه اضافه کنیم و یا از آن کم کنیم.
از استفادههای دیگری این الگو میتوان مدیریت رویدادهای یک برنامه را نام برد که با ایجاد یک رویداد متدی را فرخوانی میکند، فرم همهی توابع و اعمالی را که برای آن تعریف شده است را آگاه مینماید تا اجرا شوند. به طور کلی هرگاه بخواهیم با تغییر حالت یک شی، اشیا دیگر عمل خاصی را انجام دهند از این الگو استفاده می شود.
موارد استفاده
از این الگو در موارد زیر استفاده می شود:
وقتی که یک abstraction دارای دو جنبه باشد که یکی به دیگری وابسته باشد و هر کدام از این جنبهها در اشیا جداگانه باشند و بتوانند به طور مستقل عمل کنند و تغییرات مورد نیاز خود را روی اشیا خود اعمال کنند.
وقتی که با به وجود آمدن یک تغییر در یک شی لازم باشد که اشیا دیگر با توجه به تغییر به وجود آمده عمل خاصی انجام دهند و همچنین مشخص نیست که چندتا از اشیا باید عمل مورد نظر را انجام دهند.
وقتی که لازم باشد که یک شی بتواند به اشیا دیگر بدون توجه به ماهیت آن و یا شناختن آن اطلاع رسانی نماید.
ساختار
برای تشریح ساختار این الگو از عبارات زیر استفاده شده است:
Subject: شیئی که اشیا تغییر حالات آن را دنبال میکنند.
Observer: شی یا اشیایی هستند که تغییر حالات Subject را دنبال میکنند.
ساختار این الگو به این صورت است که هر شی Observer که بخواهد از تغییر حالت شی Subject آگاه باشد باید خود را در لیست دنبال کنندگان Subject ثبت کند. برای این کار شی Observer متد Attach شی Subject را فراخوانی میکند و خود شی را به عنوان ورودی متد میفرستد و با این کار در ساختمان داده شی Subject ثبت میشود و هر گاه که رویدادی در Subject رخ دهد، این شی متد Notify خود را فراخوانی میکند که با این کار به همه Observer ها اطلاع داده میشود. اگر یک Observer نخواهد که دیگر رویدادها را دنبال کند متدل Detach مربوط به Subject را فراخوانی میکند و خود را به عنوان ورودی به آن میفرستد و با این کار از ساختمان دادهی Subject حذف میشود و دیگر رویدادها به آن اطلاع داده نخواهد شد. همهی اشیای Observer اینترفیس IObserver را پیاده سازی میکنند. نمودار Class diagram این الگو به این شکل است.
مدل های Push و Pull
برای ارتباط بین Subject و observer دو مکانیزم وجود دارد. مکانیزم اول که Push نام دارد به این شکل است که Subject وقتی میخواهد به observer اطلاع دهد تغییرات را با تمام جزئیات میفرستد و وقتی که observer این تغییرات را دریافت میکند همهی اطلاعات را برای این که خود را update کند دارد و دیگر نیاز به اطلاعات بیشر از طرف subject نیست.
مکانیزم دوم که pull نام دارد این گونه عمل میکند که subject پیامی شبیه به آگهی به همهی observer ها میفرستد و هر کدام از observer ها که بخواهد خود را آپدیت کند، در خواست خود را به subject میفرستد تا اطلاعات مورد نیاز را بدست آورد.
الگوی observer از هر دو مدل گفته شده پشتیبانی میکند.
از مزایای این الگو می توان موارد زیر را نام برد:
این الگو باعث کم کردن وابستگی observer و subject به یکدیگر میشود و در اصطلاح این دو موجودیت در برنامه tightly couple نخواهند بود. بنابراین میتوان این دو موجودیت را در لایههای مختلف برنامه تعریف کرد و از این الگو استفاده نمود. همچنین این الگو از ارتباط broadcast پشتیبانی میکند.
برخی از مسائل پیاده سازی الگوی Observer
- همان طور که گفته شد هر subject باید لیست هر کدام از observer ها را داشته باشد و برای این کار در پیاده سازی یک ارجاع به شی مورد نظر را ذخیره کند. به منظور ذخیره این ارجاع ها باید ساختمان دادهای در نظر گرفته شود که سرعت بالایی داشته باشد تا در مواقعی که تعداد observer ها زیاد است باعث کندی برنامه نشود.
- دنبال کردن بیش از یک subject توسط observer: برای این که یک observer بتواند بیش از یک subject را دنبال کند باید متد update خود را به گونه ای تعریف کند که با هر دوی subject ها سازگاری و همخوانی داشته باشد. برای subject راحت ترین کار این است که در فراخوانی متد update خود را به عنوان پارامتر به سمت observer بفرستد و خود observer تشخیص دهد که از طرف کدام یک از subject ها پیام را دریافت کرده است.
- وقتی که عمل حذف یک subject انجام میشود، این عمل نباید باعث به وجود آمدن ارجاعهای بی فایده (Dangling reference) شود. همچنین باید ساز و کاری اندیشیده شود که وقتی که یک observer حذف میشود نیز این مشکل پیش نیاید. برای حل این مشکل در طرف observer باید هنگامی که observer در حال از بین رفتن است خود را از لیست observer های یک subject حذف کند، به این شکل که در Destructor کلاس خود متد Detach مربوط به subject را فراخوانی نماید.
- میتوان رویدادهایی که در subject به وجود میآیند را گروه بندی کرده و این قابلیت را به observer ها بدهیم تا در هنگام ثبت خود به عنوان observer مشخص کنند که قصد دنبال کردن کدام گروه را در subject دارند.
- به علت پیچیدگیهایی که گفته شد ممکن است برای برقراری ارتباط بین observer و subject یک شی جدید به نام ChangeManager داشته باشیم که همهی پیچیدگیهای ارتباط را به این شی محول کنیم. که با این کار مدیریت مسائل گفته شده بسیار آسان تر خواهد بود. البته این کلاس ChangeManger میتواند یک نمونه از الگوی Mediaor باشد و به طور طبیعی باید تنها یک ChangeManager بین یک مجموعه observer و subject موجود باشد که برای این کار میتوان از الگوی Singleton استفاده کرد.
پیاده سازی
پایه و اساس پیاده سازی این الگو به همان شکلی است که class diagram آن نشان داده شده است. بنابراین اگر بخواهیم این الگو را با زبانی مانند Java پیاده سازی کنیم کد آن به شکل زیر خواهد شد.
public interface IObserver {
public void update();
}
public class Observer implements IObserver {
public Observer(Subject subject) {
subject.attach(this);
}
public void update() {
System.out.printf("Call update");
}
}
public class Subject {
Set<IObserver> IObserverSet = new HashSet<IObserver>();
public void attach(IObserver IObserver) {
IObserverSet.add(IObserver);
}
public void detach(IObserver IObserver) {
IObserverSet.remove(IObserver);
}
private void notifies() {
for (IObserver IObserver : IObserverSet) {
IObserver.update();
}
}
public void message() {
System.out.println("Hello");
notifies();
}
}
public class App {
public static void main(String[] args) {
Subject subject = new Subject();
Observer observer = new Observer(subject);
subject.message();
}
}
در مثال ساده فوق، هر زمانی که متد message فراخوانی شود متد update تمام آبجکت های Observer فراخوانی میگردد و پیام Call update چاپ میگردد.
مثال پیاده سازی شده با زبان جاوا را میتوانید از github من دانلود کنید.
اگر قبلا در بیان ثبت نام کرده اید لطفا ابتدا وارد شوید، در غیر این صورت می توانید ثبت نام کنید.