دیزاین پترن Chain of Responsibility در اندروید

دیزاین پترن Chain of Responsibility  در اندروید
فهرست مقاله [نمایش]

    بزرگترین چالش در ساخت برنامه های اندرویدی ، چالش کار با سایر توسعه دهندگان است. مطمئناً، مدیریت روش های چرخه حیات خسته کننده است. ساختن custom view که layout را خراب نمی کنند و render cycle ها نمی توانند پیچیده باشند. و نگه داری از برنامه های در حال گسترش همیشه یک کار تمام وقت است.

    اما هیچ کاری در دنیای توسعه اندروید به اندازه انجام همه این کارها به طور هم زمان و در همان پایه کد دشوار نیست. مشکل همزمان کار کردن با سایر توسعه دهندگان Android با نوشتن سیستم های همزمان تفاوتی ندارد. هنگام نوشتن سیستم های همزمان، مسئله اصلی حالت مشترک است.

    هنگام نوشتن برنامه های Android با سایر توسعه دهندگان مسئله اصلی این است که همه کد ها مشترک است. چگونه همزمانی حالت مشترک را اداره می کند؟برنامه نویسی کاربردی پارادایم را ایجاد می کند که در آن هیچ حالت مشترک وجود ندارد ،بنابراین نگرانی بابت هم زمانی وجود ندارد.

    این مقاله بحثی در مورد چگونگی استفاده از دیزاین پترن Chain of Responsibility هنگام ساخت برنامه های اندرویدی است.

    Chain of Responsibility Pattern

    Chain of Responsibility یک دیزاین پترن است که "event" ها را به روشی ماژولار اجرا می کند. با استفاده از Chain of Responsibility ، فرستنده و تولید کننده یک رویداد نیازی به دانستن object های مربوط به آن رویداد ندارد. علاوه بر این ، هر کنترل کننده احتمالی یک رویداد نیازی به دانش در مورد سایر اجزا ندارد. بنابراین اجزا به راحتی به Chain of Responsibility اضافه می شوند بدون اینکه روی رفتار کلاینت یا هر handler تأثیر بگذارد.

    از نظر ساختاری یک Chain of Responsibility مانند لیست کاملاً مرتبط با object های handler به نظر می رسد. هنگامی که یک رویداد نیاز به handel شدن دارد به اولین handler که در لیست قرار دارد سپرده میشود. handler اول تلاش می کند تا این رویداد را کنترل کند - در صورت دستیابی به رویداد ، اجرای آن متوقف می شود، اما اگر این رویداد توسط handler اول صورت نگیرد، آن را به handler دوم منتقل می کند. این روند تکرار میشود تا زمانی که هیچ handler برای دریافت رویداد وجود نداشته باشد.

    شما ممکن است Chain در جاوا را به روش زیر پیاده سازی کنید:

    BaseHandler یک abstract class است که منطق متداول را برای تعیین یک "successor" در لیست تعریف می کند.

     

    public abstract class BaseHandler {
        private BaseHandler mSuccessor;
        public void setSuccessor(BaseHandler successor) {
            mSuccessor = successor;
        }
        protected boolean sendToSuccessor(Object event) {
            if (null != mSuccessor) {
                return mSuccessor.handleEvent(event);
            } else {
                return false;
            }
        }
        abstract public boolean handleEvent(Object event);
    }
    

    ConcreteHandler که در پایین می بینید نمونه ای از زیر کلاس اجرای BaseHandler است. Implementation ها وظیفه دارند تصمیم بگیرند که آیا می توانند یک رویداد خاص را انجام دهند و سپس اقدام مورد نظر را انجام دهند. اگر رویداد معین توسط یک handler مشخص قابل اجرا نباشد، باید به فهرست بعدی در لیست ارسال شود.

    public class ConcreteHandler extends BaseHandler {
        @Override
        public boolean handleEvent(Object event) {
            if (canHandle(event)) {
                // Handle the event.
                return true;
            } else {
                return sendToSuccessor(event);
            }
        }
    

    chain سپس می تواند به روش زیر ایجاد و استفاده شود.

    private void createChain() {
        ConcreteHandler1 handler1 = new ConcreteHandler1();
        ConcreteHandler2 handler2 = new ConcreteHandler2();
        handler1.setSuccessor(handler2);
        ConcreteHandler3 handler3 = new ConcreteHandler3();
        handler2.setSuccessor(handler3);
        mChain = handler1;
    }
    private void onEvent(Object event) {
        boolean wasHandled = mChain.handleEvent(event);
        if (!wasHandled) {
            // No handlers could handle the event, do whatever exception
            // handling here that makes sense.
        }
    }
    

    چگونه ممکن است در کار روزانه Android از chain استفاده کنیم؟

    Handling Push Notifications هندل کردن نوتیفیکیشن

    نوتیفیکیشن هایی را که برنامه شما دریافت می کند در نظر بگیرید. این پیام ها می توانند از نظر نوع پیام و ساختار بار متفاوت باشند. مطمئناً می خواهید پیام های مختلفی را به روش های بسیار متفاوتی مدیریت کنید. شاید یک پیام به نمایش یک نوتیفیکیشن نیاز داشته باشد.اگر یک پیام دیگر بیاید نیاز به یک service دارد که در بک گراند اجرا شود. نوع سوم پیام نیاز دارد تا یک دیالوگ را در قسمت Activity قابل مشاهده به کاربر نشان دهد.

    یک رویکرد ساده به مسئله نوتیفیکیشن ممکن است شبیه موارد زیر باشد.

    // This is in whatever Object receives push notifications.
    switch (MESSAGE_TYPE) {
            case NOTIFICATION:
                // Do notification
                break;
            case BACKGROUND_SERVICE:
                // Start background service
                break;
            case ALERT_DIALOG:
                // Show alert dialog
                break;
        }
    

    این راه حل که ساده به نظر می رسد ، اما چرا مشکل ساز است؟

    question

     

    خب ، در نظر بگیرید که ما تصوری نداریم که چه تعداد رفتار جدید پیام را با گذشت زمان به این کنترل کننده اعلان فشار اضافه خواهیم کرد. هر بار که نیاز به یک رفتار جدید باشد، یک توسعه دهنده این کلاس را باز کرده و این کد را که نوتیفیکیشن را پردازش می کند ، تغییر می دهد. این یک نقض آشکار از اصل open/close است.ما نمیتوانیم دیوار بین برنامه نویسان نصب کنیم به گونه ای که اکنون چندین توسعه دهنده ممکن است در هنگام اجرای ویژگی های جدید و متنوع رویکرد های متفاوت خود را در حال جهش در محتوای این کلاس پیدا کنند. برای کمک به جلوگیری از درگیری های آینده بین توسعه دهندگان، باید از یک ساختار کد استفاده کنیم که تضاد را قبل از وقوع به طور کامل برطرف کند.

    علاوه بر این راه حل ساده سناریویی را ایجاد می کند که وابستگی کد به مرور زمان نمایان شود. انواع وابستگی هایی را که قبلاً برای دستیابی به رفتار موجود مورد نیاز است در نظر بگیرید: contextها ، activityها ، service ها ،pendingintent ها ، notification ها، dialog و غیره. چه تأثیری در توانایی ما برای تست این کلاس می تواند داشته باشد؟ اگر می خواهید تست کنید که یک background service آغاز شده است، ممکن است شما نیاز به ارائه مکانیزمی برای نمایش dialog ها داشته باشید حتی اگر با چیزی که تست می کنید کاملاً بی ربط است. علاوه بر این، هنگامی که چندین توسعه دهنده به طور معمول رفتار مستقلی را به همان کلاس اضافه می کنند، تمایل به ایجاد اتصالات کاملاً غیر ضروری بین سیستم های نامربوط وجود دارد. با توجه به زمان کافی، شروع background service در مثال قبلی راهی برای وابستگی به برخی از جزئیات نمایش dialog پیدا می کند. با انباشت کد در یک مکان، میانبرها ظاهر می شوند و میانبرها برداشته می شوند. پس از مدتی قادر نخواهید بود رفتار background service را از dialog و رفتار notification جدا کنید.

    به جای اینکه به سمت یک درگیری بروید ، هندل کردن notification را با یک خط کد جایگزین کنید:

    
    mPushNotificationChain.handlePushNotification(payload);
    

    سپس هر handler را در Object خود جدا کنید.

    // DisplayNotification.java
    public class DisplayNotification extends PushNotificationHandler {
        @Override
        public boolean handlePushNotification(String payload) {
            // Show an Android notification, or pass to successor.
        }
    }
    // StartBackgroundService.java
    public class StartBackgroundService extends PushNotificationHandler {
        @Override
        public boolean handlePushNotification(String payload) {
            // Start a background service, or pass to successor.
        }
    }
    // ShowDialog.java
    public class ShowDialog extends PushNotificationHandler {
        @Override
        public boolean handlePushNotification(String payload) {
            // Show a dialog, or pass to successor.
        }
    

    اکنون هر بار که یک توسعه دهنده نیاز به ایجاد یک push notification جدید دارد، یک فایل Class ایجاد کرده و آن رفتار را implement می کند.

    سپس توسعه دهنده دو خط کد به کلاس handler اصلی اضافه می کند تا بتوانید handler جدید را به Chain of Responsibility اضافه کنی بدون وجود کد بیشتر، بدون وابستگی متقاطع تصادفی.

    Android Service Requests

    درخواست های سرویس

    Android notification handling بسیار شبیه به کار هایی است که ما به طور مرتب در توسعه Android انجام می دهیم.

    هر سرویس Android که ایجاد می کنیم باید به درخواست های مختلف رسیدگی کند این درخواست ها توسط Intent هایی که در ()onStartCommand اجرا می شوند.

    بسیاری از service های ما مجموعه ای از درخواست های خاص دارند که هرگز نیازی به تغییر ندارند. هنگامی که option ها در stone تنظیم شده باشد، احتمالاً chain of responsibilities مورد نیاز نیست. اما اگر سرویس هایی دارید که می توانید درخواست responsibilitie های جدیدی را اضافه کنید ، پس از استفاده ازChain of Responsibility در نظر بگیرید. متد ()onStartCommand قابل اجرا است.

    با یک خط کد

    mCommandChain.handleCommand(intent);

     

    هنگامی که باید درخواست جدیدی به سرویس خود اضافه کنید ، کافیست یک Object Handler جدید ایجاد کرده و آن را به Chain of Responsibility خود اضافه کنید. کجا از Chain of Responsibility استفاده کنیم؟

    مثال های موجود در این مقاله فقط چند مورد از نیازهای روزمره اندرویدی است که می تواند ازChain of Responsibility بهره مند شود.

    شما کجا از Chain of Responsibility استفاده میکنید؟ در بخش نظرات توضیح بدهید.

    ممنون که با ما همراه بودید امیدوارم که این مقاله براتون مفید بوده باشه.

    اطلاعات نویسنده
    • نویسنده: میثم بابائی

    ارسال دیدگاه

    برای افزودن دیدگاه خود، نیاز است ابتدا وارد حساب کاربری‌تان شوید


    دیدگاه کاربران