آشنایی با الگوهای طراحی Observer در اندروید

نویسنده : سید ایوب کوکبی ۲۷ فروردین ۱۳۹۸
آشنایی با الگوهای طراحی Observer در اندروید

Observer یکی از محبوب‌ترین الگوهای طراحی نرم‌افزار بوده که کاربردهای مختلفی دارد. اجازه دهید ابتدا کمی از الگوهای طراحی صحبت کنیم. در مهندسی نرم‌افزار الگوهای طراحی یا Design Pattern به الگوها و راهکارهای مشخصی گفته می‌شود که برای حل مشکلات رایج در برنامه‌نویسی شی‌ءگرا استفاده می‌شود. دیزاین پترن‌ها در ابتدا به صورت پراکنده از سوی شخصی به نام کریستوف الکساندر مطرح شد و بعدها توسط گروه GOF در کتابی تحت عنوان الگوهای طراحی طبقه‌بندی شد.

به عنوان مثال بارها خواسته‌اید، از روی یک کلاس فقط یک آبجکت نمونه‌سازی شود مثلاً صفحۀ اصلی برنامه. کسی دوست ندارد چند نسخه از برنامه‌اش به صورت همزمان اجرا شود. یا مثلاً برای عمل logging، معمولاً همۀ لاگ‌ها داخل یک محل مشخص ذخیره می‌شود. وقتی در قسمتی از برنامه عمل لاگ انجام می‌شود یعنی از قبل فایل لاگ باز شده و اگر جای دیگری دوباره به لاگ نیاز داشتیم همان فایل قبلی را فرخوانی می‌کنیم. یا وقتی به دیتابیس متصل می‌شوید دیگر نیاز نیست در قسمت‌های دیگر برنامه کانکشن دیگری به دیتابیس باز کنیم. همۀ این‌ها یک مسئلۀ مشترک دارند که راهکارش الگوی Singleton است.

Observer نیز الگوی طراحی مشهور دیگری است که ایده‌اش ارتباط یک به چند بین اشیاء است. گاهی لازم است با تغییر یک آبجکت سایر آبجکت‌های وابسته مطلع شوند. دقیقاً مثل خبرنامۀ سایت، کاربران ایمیل خود را وارد می‌‌کنند و هر وقت خبر جدیدی شد همۀ اعضاء مطلع می‌شوند. Observer یک الگوی رفتاری یا Behavioral است. 

در این الگو یک آبجکت Observable داریم که گاهی اوقات Subject هم نامیده می‌شود. این همان آبجکتی است که اشیاء Observer یا Client در آن عضو یا subscribe می‌کنند تا اگر تغییری حاصل شد با خبر شوند. در این الگو می‌توانیم یک Observable و چندین Observer داشته باشیم. دلیل یک به چند بودن رابطه هم همین است. observerها هیچ اطلاعی هم از یکدیگر ندارند و هر وقت بخواهیم می‌توانیم با لغو عضویت، تغییرات آتی را دریافت نکنیم دقیقاً مثل خبرنامه که هر وقت بخواهیم unsubscribe می‌کنیم.

بخوانید  بهینه‌سازی بازیهای اندروید در یونیتی

الگوی Observer راهی عالی برای پیاده‌سازی اصل (OCP (the open/closed principle است که یکی از اصول بنیادی SOLID است. این اصل بیانگر این است که برنامه باید برای توسعه باز باشد ولی برای اصلاح بسته. مثالش را هم قبلاً زده‌ایم.

مثالی واقعی از Observer

فرض کنید دکمه‌ای دو وضعیتی یا اصطلاحاً ToggleButton دارید. یعنی با کلیک اول On می‌شود و با کلیک دوم مجدداً Off. در الگوی طراحی Observer به این دکمه Subject گفته می‌شود. از طرفی فرگمنت‌هایی داریم که می‌خواهیم به تغییرات این دکمه عکس‌العمل نشان دهند. مثلاً هر وقت دکمه ON شد پیغامی نمایش داده شود یا رنگ زمینه عوض شود و … . ما می‌خواهیم با تغییر وضعیت دکمه در هر فرگمنت یک پیام متفاوت به کاربر نمایش داده شود. به این فرگمنت‌ها که به وضعیت دکمه چشم دوخته‌اند observer گفته می‌شود.

الگوی observer همچنین امکان unsubscribe کردن observerها را هم به ما می‌دهد. مثلاً وقتی فرگمنت‌ها visible نیستند دلیلی ندارد پیامی نمایش دهیم پس لغو عضویت می‌کنیم. پس به طور خلاصه، سه فرگمنت داریم: A و B که observerهای ما هستند و منتظر تغییر وضعیت دکمه در فرگمنت C (یا subject) هستند. هر سه فرگمنت داخل یک اکتیویتی قرار گرفته‌اند.

پیاده‌سازی

ابتدا برای observer و subject یک اینترفیس درست می‌کنیم.

سابجکت کارهایی مثل عضویت، لغو عضویت و اطلاع‌رسانی observerها را انجام می‌دهد:

public interface Subject {
    public void register(Observer observer);
    public void unregister(Observer observer);
    public void notifyObservers();
}

Observer هم کار بروزرسانی و دریافت جدیدترین داده‌ها از سابجکت را انجام می‌دهد:

public interface Observer {
    public void update(final boolean checked);
}

اکنون این دو اینترفیس را باید در جای درستی پیاده‌سازی کنیم. سابجکت ما فرگمنت C بود چون ToggleButton داخل آن قرار دارد:

public class ThirdFragment extends Fragment implements Subject {

    private List<Observer> observers;
    private ToggleButton button;
    
    public ThirdFragment() {
        observers = new ArrayList<>();
    }
    @Override
    public void onViewCreated(final View view, @Nullable final
    Bundle savedInstanceState) {
        button = (ToggleButton)
            view.findViewById(R.id.toggle_button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View v) {
                notifyObservers();
            }
        });
    }
    @Override
    public void register(final Observer observer) {
        if (!observers.contains(observer)) {
            observers.add(observer);
        }
    }

    @Override
    public void unregister(final Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (final Observer observer : observers) {
            observer.update(button.isChecked());
        }
    }
}

همانطور که می‌بینید این فرگمنت سه متد برای عضویت، لغو عضویت و اطلاع‌رسانی observerها را پیاده‌سازی کرده است. برای دکمه یک listener تعریف شده است که به محض فرخوانی رویداد کلیک، متد notifyObserver فرخوانی می‌شود. و این متد نیز به نوبۀ خود متد update تمامی observerها را با وضعیت جدید دکمه مقداردهی می‌کند. خب کار با این فرگمنت تمام است. برویم سراغ پیاده‌سازی observer.

public class SecondFragment extends Fragment implements Observer {

    ...

    @Override
    public void update(final boolean checked) {
        if (checked) {
            textView.setText("ON");
        } else {
            textView.setText("OFF");
        }
    }
}

هر تغییری در دکمه ایجاد شود متد update فرخوانی می‌شود که از طریق آن می‌توانیم عناصر خاصی در واسط گرافیکی برنامه را تغییر دهیم. در اینجا بر اساس آخرین وضعیت دکمه، پیغام ON یا OFF نمایش داده می‌شود. شما می‌توانید هر کار دیگری هم انجام دهید. مثلاً وقتی دکمه غیرفعال شد رنگ زمینه فرگمنت عوض شود یا المان دیگری غیرفعال شود یا هر کار دیگری…

بخوانید  کاهش حجم فایل‌های PNG

خب حالا باید همۀ این فرگمنت‌ها را داخل یک اکتیویتی قرار دهیم و فرگمنت‌های اول و دوم را در فرگمنت سوم که subject است عضو کنیم؛ به این صورت:

public class ObserverActivity extends AppCompatActivity {

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ...

        thirdFragment.register(firstFragment);
        thirdFragment.register(secondFragment);
    }
}

مشاهده می‌کنید فرگمنت A و B از طریق متد register در فرگمنت C عضو شده‌اند؛ بنابراین از آخرین وضعیت فرگمنت C مطلع خواهند شد. این نکته را به یاد داشته باشید که وقتی کارتان تمام شد حتماً با متد unregister آبجکت‌هایی غیرضروری را از فهرست اعضای subject خارج کنید تا با مشکل نشت حافظه مواجه نشوید.

نتیجه‌گیری

الگوی طراحی Observer راه خوبی برای loose coupling است؛ یعنی رابطۀ بین اجزاء برنامه را به حداقل ممکن می‌رساند. این کار ریسک اثر تغییر یک جزء بر سایر اجزاء را کاهش می‌دهد. در این الگو، هر تعداد observer می‌توانند در subject عضو شوند و از آخرین تغییرات سابجکت مطلع شوند بدون اینکه از یکدیگر خبری داشته باشند. در یک کلام بهترین روش پیاده‌سازی ارتباط یک به چند یا on-to-many، الگوی طراحی Observer است.

  

سید ایوب کوکبی

نویسنده و مترجم...

مطالب مرتبط

آموزش گیت – قسمت یازدهم

۲۷ اردیبهشت ۱۳۹۸

آموزش گیت – قسمت دهم

۲۷ اردیبهشت ۱۳۹۸

0 دیدگاه

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *