بیاید الگوی طراحی Observer یا به انگلیسی Observer Pattern رو با یک داستان شروع کنیم.

فرض کنید شما میخواهید یه لپ تاپ خیلی خیلی جدید بخرید. کل اینترنت رو زیر رو می کنید اما لپتاپ مورد نظر شما اینقدر جدیده که هنوز هیچ سایتی اون رو برای فروش قرار نداده و همه از مشخصه های لپ تاپ حرف زدن. بعد از کلی گشت و گذار بین فروشگاه های اینترنتی بالاخره یک فروشگاه رو پیدا میکنید که لپ تاپ مورد نظر شما رو در لیست محصولات فروشگاه اش قرار داده اما یک مشکلی هست... لپتاپ هنوز در انبار فروشگاه موجود نشده و شما باید منتظر بمونید.

فروشگاه امکانی رو فراهم کرده تا شما آدرس ایمیل یا شماره موبایل خودتون رو برای یک محصول خاص که در انبار نیست درج کنید تا به محض اینکه محصول در انبار موجود شد از طریق ایمیل یا پیام کوتاه به شما اطلاع داده بشه. اما شما تنها فردی نیستید که برای خرید اون لپتاپ عجله دارید؛ بجز شما ۴ نفر دیگه هم درخواست دادن تا بلافاصله بعد از موجود شدن محصول از اون با خبر بشن.

یک هفته میگذره و خبری از موجود شدن لپتاپ در انبار فروشگاه اینترنتی نیست. یکی از اون ۴ نفر خیلی خیلی به لپتاپ جدید نیاز داره و کلی از کارهاش عقب افتاده و بیشتر از این نمیتونه منتظر این لپتاپ باشه. پس وارد سایت فروشگاه شده و اطلاع رسانی زمان موجود شدن محصول انصراف میده و شماره موبایل خودش رو از زیر محصول حذف میکنه. چند روز به هم لپتاپ جدید در انبار فروشگاه موجود میشه و همه افراد باقی مونده در صف اطلاع رسانی با خبر میشن و لپتاپ مورد نظر خودشون رو خریداری میکنن.


خوب حالا وقتشه که با تمام قدرت بریم سر وقت الگوی طراحی Observer:

در داستانی که گفته شد تمام افرادی که شماره تلفن همراه یا ایمیل خودشون رو برای لپتاپ جدید ثبت کرده بودن تا در زمان موجود شدنش با خبر بشن یک ماهیت Observer هستند و لپتاپی که همه به دنبال خریدنش بودن یک ماهیت Observable است.

همان طور که از داستان متوجه شدید در الگوی طراحی Observer یک Observable می تواند چند Observer داشته باشد و زمانی که وضعیت Observable تغییر کرد تمامی Observer های باقی مانده آن از تغییر با خبر می شوند (بهتره بگیم notify می شوند)؛ حالا چرا از لفظ باقی مانده استفاده کردیم؟ چون ممکنه یک یا چند تا از Observer ها از مطلع شدن انصراف بدند. (مثل فردی که برای خرید لپتاپ عجله داشت)

با توجه به همه این صحبت هایی که شد ۴ ویژگی اصلی برای پیاده سازی وجود دارد:

۱- هر Observable می تواند چند Observer داشته باشد.

۲- Observer ها را بتوان به لیست اضافه کرد.

۳- Observer ها را بتوان از لیست حذف کرد.

۴- وقتی که تغییراتی رخ داد Observer ها از تغییر باخبر شوند.

توی دنیای واقعی این الگو را میتونیم برای شبکه خبری؛ شبکه اجتماعی و هر سیستمی که از همین رفتار پیروی میکنه پیاده استفاده کنیم. مثلا توی facebook یا twitter زمانی که یک نفر وضعیت اش رو بروزرسانی میکنه تمام follower هاش از تغییر وضعیت باخبر میشن. یه شخص می توانه شخص دیگه ای رو follow کنه تا از تغییر وضعیتش باخبر بشه یا با unfollow کردنش از دریافت تغییر خبر انصراف بده.

اما توی برنامه نویسی و ابزارهای تولید شده هم از observer pattern استفاده شده. Observer pattern پایه تمام سیستم هایی است که بر مبنای message کار می کنند. مثلا HornetQ یا معروف تر از اون برای ما برنامه نویس های جاوا JMS که همه از همین الگوی طراحی استفاده میکنن. بعلاوه توی رابط کاربری java هم از Observer pattern استفاده میشه زمانی که دکمه keyboard رو فشار میدید یا mouse رو کلیک میکنید هم Observer Pattern حرف اول رو در طراحی میزنه

ما هم برای این که حوصله اتون سر نره یه مثال دیگه می زنیم


دیاگرام ترتیب عملیات (به انگلیسی Sequence Diagram) الگوی طراحی Observer:

الگوی طراحی Observer


کلاس دیاگرام (به انگلیسی class diagram) الگوی طراحی Observer:


الگوی طراحی Observer


نسخه جاوایی پیاده سازی شده از یه خبرگزاری به صورت خیلی خیلی ساده:

public interface Subject 
{
    public void attach(Observer o);
    public void detach(Observer o);
    public void notifyUpdate(Message m);
}


public class MessagePublisher implements Subject {
     
    private List<Observer> observers = new ArrayList<>();
 
    @Override
    public void attach(Observer o) {
        observers.add(o);
    }
 
    @Override
    public void detach(Observer o) {
        observers.remove(o);
    }
 
    @Override
    public void notifyUpdate(Message m) {
        for(Observer o: observers) {
            o.update(m);
        }
    }
}


public interface Observer 
{
    public void update(Message m);
}


public class MessageSubscriberOne implements Observer 
{
    @Override
    public void update(Message m) {
        System.out.println("MessageSubscriberOne :: " + m.getMessageContent());
    }
}


public class MessageSubscriberTwo implements Observer 
{
    @Override
    public void update(Message m) {
        System.out.println("MessageSubscriberTwo :: " + m.getMessageContent());
    }
}


public class MessageSubscriberThree implements Observer 
{
    @Override
    public void update(Message m) {
        System.out.println("MessageSubscriberThree :: " + m.getMessageContent());
    }
}


public class Message 
{
    final String messageContent;
     
    public Message (String m) {
        this.messageContent = m;
    }
 
    public String getMessageContent() {
        return messageContent;
    }
}


public class Main 
{
    public static void main(String[] args) 
    {
        MessageSubscriberOne s1 = new MessageSubscriberOne();
        MessageSubscriberTwo s2 = new MessageSubscriberTwo();
        MessageSubscriberThree s3 = new MessageSubscriberThree();
         
        MessagePublisher p = new MessagePublisher();
         
        p.attach(s1);
        p.attach(s2);
         
        p.notifyUpdate(new Message("First Message"));   //s1 and s2 will receive the update
         
        p.detach(s1);
        p.attach(s3);
         
        p.notifyUpdate(new Message("Second Message")); //s2 and s3 will receive the update
    }
}


در خروجی چنین نتیجه ای خواهید داشت:

MessageSubscriberOne :: First Message
MessageSubscriberTwo :: First Message
 
MessageSubscriberTwo :: Second Message
MessageSubscriberThree :: Second Message


جاوا ملزومات این design pattern رو در JDK آماده کرده؛ اینترفیس Observer و کلاس Observable. اما از اونجایی که پیاده سازی خوبی نداشت (برنامه نویس ها میتونن بعضی از متدهای synchronized در کلاس Observable رو override کنن و این باعث میشه thread-safety از بین بره این موضوع یکی از دلایلی بود که تصمیم به تغییر در این کلاس ها شد.) در نسخه 9 جاوا Depricated اعلام شد و به جای اون از اینترفیس PropertyChangeListener میتونید استفاده کنید که به جای Observer پیشنهاد شده است.