MVP در اندروید با یک مثال ساده

نویسنده : سید ایوب کوکبی ۱۸ فروردین ۱۳۹۸

MVP در اندروید با یک مثال ساده

MVP یا Model-View-Presenter یک الگوی معماری است که برای جداسازی منطق برنامه از رابط کاربری به کار می‌رود. این الگو فرم خاصی از معماری MVC است که اغلب برای ساخت رابط کاربری استفاده می‌شود. اینجا به جای کنترلر از Presenter یا نمایش به عنوان لایۀ میانی استفاده می‌کنیم. در MVP هر چیزی که مربوط به رابط گرافیکی باشد در لایۀ نمایش قرار می‌گیرد. این الگو طرفدار جداسازی لایۀ داده از منطق تجاری برنامه است و مایل است کدها را به خارج از اکتیویتی و فرگمنت منتقل نماید.

تفاوت بین MVC و MVP

تفاوت MVP و MVC

Model-View-Controller

  • کنترلر رفتار محور بوده و می‌تواند بین چندین ویو به اشتراک گذاشته شود؛
  • ویو به صورت مستقیم می‌تواند به مدل دسترسی داشته باشد.

Model-View-Presenter

  • ویو کاملاً از مدل جداست و ارتباط بین این دو تنها از طریق لایۀ نمایش برقرار می‌شود؛
  • انجام یونیت تست در این الگو راحت‌تر است؛
  • عموماً بین لایه ویو و نمایش رابطه‌ای یک به یک وجود دارد ولی می‌توان برای ویوهای پیچیده از چندین Presenter مستقل بهره جست؛
  • به فعالیت‌های کاربر توجه دارد و متناسب با آن مدل را آپدیت می‌کند؛
  • تغییر داده‌های مدل باعث بروزرسانی ویو می‌شود.

Model

در یک برنامۀ چندلایۀ خوب، مدل تنها راه دسترسی به داده‌هاست. در واقع ارتباط با دیتابیس محلی، دیتابیس راه دور، فراخوانی API، کشینگ داده‌ها، مدیریت دیتابیس و هر چیزی که مربوط به داده باشد در این لایه مدیریت می‌شود. اشکالات و خطاهای مربوط به دیتابیس و ارتباط با سرور در این لایه بررسی می‌شود. لایۀ مدل معمولاً کمترین میزان کد را داراست.

View

ویو معمولاً به یک اکتیویتی اشاره دارد که شامل ارجاعی به لایه نمایش است. تنها کاری که انجام می‌دهد، فرخوانی متدها از لایۀ نمایش است. در MVP وظیفۀ بروزرسانی عناصر UI تنها بر عهدۀ لایۀ نمایش است. ویو حق ندارد داده‌ای را از طرف خودش داخل ویو نمایش دهد. هر درخواستی دارد باید به لایۀ Presenter ارجاع دهد.

بخوانید  آموزش زبان کاتلین – درس 24 (کلاسهای تودرتو یا داخلی)

Presenter

Presenter یا لایۀ نمایش، واسطی بین ویو و مدل است. داده‌ها را از مدل می‌گیرد و متناسب با ویوی مقصد نمایش می‌دهد. مثلاً فهرست کشورها را از دیتابیس دریافت کرده و متناسب با نوع ویو (مثلاً Spinner، ListView و …) قالب‌بندی و نمایش می‌دهد. فرق Presenter در MVP با Controller در MVC این است که کنترلرِ MVC کاری به اینترکشن‌های کاربر ندارد ولی Presenter به رفتار کاربر و عملی که انجام می‌دهد آگاه است و تغییرات اعمال شده در ویو را می‌تواند در مدل منعکس کند.

مثال ساده‌ای از MVP

پروژه‌ای که می‌خواهیم توضیح دهیم فقط یک صفحه دارد. دو تا EditText برای username و email و یک TextView برای نمایش نام و ایمیل وارد شده. وقتی کاربر یوزرنیم و ایمیل را وارد می‌کند. داده‌های ورودی در تکست‌ویوی بالای صفحه نمایش داده می‌شوند. برای سهولت بیشتر برای مدل از دیتابیس استفاده نکردیم ولی شما به سادگی می‌توانید این کار را خودتان امتحان کنید. تغییرات چندانی لازم نیست.

ابتدا با پایین‌ترین لایه یعنی مدل شروع کنیم:

/**
 * Created by bpn on 11/30/17.
 */

public class User {

    private String fullName = "", email = "";

    public User() {
    }

    public User(String fullName, String email) {
        this.fullName = fullName;
        this.email = email;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "Email : " + email + "\nFullName : " + fullName;
    }
}

تابع toString معادل رشته‌ای آبجکت ساخته شده را در صورت تقاضا برمی‌گرداند که ترکیب مناسبی از نام و ایمیل کاربر است.

حالا به لایۀ نمایش یا Presenter می‌رسیم:

public class MainActivityPresenter {

    private User user;
    private View view;

    public MainActivityPresenter(View view) {
        this.user = new User();
        this.view = view;
    }

    public void updateFullName(String fullName){
        user.setFullName(fullName);
        view.updateUserInfoTextView(user.toString());

    }

    public void updateEmail(String email){
        user.setEmail(email);
        view.updateUserInfoTextView(user.toString());

    }

    public interface View{

        void updateUserInfoTextView(String info);
        void showProgressBar();
        void hideProgressBar();

    }
}

همانطور که می‌دانید لایۀ نمایش از خودش هیچ داده‌ای تولید نمی‌کند. فقط کارش این است که داده‌ها را از مدل بگیرد و به صورت مناسبی در ویو نمایش دهد. به خاطر این ارتباط دو طرفه باید آبجکتی از مدل و ویو در ابتدای کلاس داشته باشیم؛ یک آبجکت از User و یکی هم از View. سپس از طریق سازندۀ کلاس، ویوی مورد نظر دریافت شده و در متغیر view قرار می‌گیرد. همچنین آبجکت جدیدی از User ساخته‌ایم. در متد updateFullName نام کاربر دریافت شده و پس از بروزرسانی مدل، نمایش داده می‌شود. دقت کنید Presenter خودش به صورت مستقیم رشتۀ دریافتی را به ویو نمی‌فرستد. بلکه ابتدا لایۀ مدل را بروزرسانی می‌کند و سپس از داده‌های بروز شدۀ مدل برای نمایش در ویو استفاده می‌کند. اینترفیسی هم داخل این کلاس تعریف کرده‌ایم تا کار نمایش داده‌های جدید و پروگرس‌بار را در لایۀ ویو انجام دهد.

بخوانید  ساخت اپلیکیشن با بودجه محدود

آخرین لایه، ویو است:

public class MainActivity extends AppCompatActivity implements MainActivityPresenter.View {

    private MainActivityPresenter presenter;
    private TextView myTextView;
    private ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        presenter = new MainActivityPresenter(this);

        myTextView = findViewById(R.id.myTextView);
        EditText userName = findViewById(R.id.username);
        EditText email = findViewById(R.id.email);
        initProgressBar();


        userName.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                presenter.updateFullName(s.toString());
            }

            @Override
            public void afterTextChanged(Editable s) {
                hideProgressBar();
            }
        });

        email.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                presenter.updateEmail(s.toString());
            }

            @Override
            public void afterTextChanged(Editable s) {
                hideProgressBar();
            }
        });

    }

    private void initProgressBar() {
        progressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleSmall);
        progressBar.setIndeterminate(true);
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(Resources.getSystem().getDisplayMetrics().widthPixels,
                ۲۵۰);
        params.addRule(RelativeLayout.CENTER_IN_PARENT);
        this.addContentView(progressBar, params);
        showProgressBar();
    }

    @Override
    public void updateUserInfoTextView(String info) {
        myTextView.setText(info);
    }

    @Override
    public void showProgressBar() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideProgressBar() {
        progressBar.setVisibility(View.INVISIBLE);
    }
}

در اکتیویتی فوق، اینترفیس MainActivityPresenter.View را پیاده‌سازی می‌کنیم. لایۀ ویو فقط با Presenter در ارتباط هست، بنابراین تنها چیزی که نیاز داریم، ساخت یک آبجکت از کلاس Presenter است. داخل متد onCreate مقدار این آبجکت را new می‌کنیم و ویوی جاری را به آن ارسال می‌کنیم. با تعریف textListener روی فیلد userName و email تغییرات را به Presenter گزارش می‌دهیم. دقت کنید لایۀ ویو فقط رشته‌ای که تغییر کرده را به لایۀ نمایش گزارش می‌دهد؛ دیگر کاری ندارد که لایۀ نمایش می‌خواهد با این داده‌ها چه کند. در انتها اینترفیس را پیاده‌سازی کرده‌ایم. متد updateUserInfoTextView اینفو دریافتی را در تکست‌ویو نمایش می‌دهد. info ترکیبی از نام و ایمیل کاربر است که قبلاً متد toString آن را در کلاس User نوشتیم. پروگرس‌بار شاید در این مثال کاربرد نداشته باشد ولی اگر داده‌های مدل از سرور دریافت می‌شد به راحتی می‌توانستید ضرورت آن را احساس کنید. با ایجاد تأخیر مصنوعی هم می‌توانید پروگرس‌بار را بیشتر به نمایش درآورید.

بخوانید  امکانات مخفی اندروید استودیو

کدها را خودتان بررسی کنید؛ بهتر از توضیحاتی که دادیم متوجه می‌شوید 🙂 نکتۀ جالب اینکه به راحتی می‌توانید لایۀ مدل را طوری تغییر دهید که داده‌ها را از دیتابیس استخراج کرده و در آن ذخیره کند. این دیتابیس می‌تواند لوکال باشد یا به صورت ریموت روی سرور قرار داشته باشد. شاید استفاده از الگوی MVP یا سایر متدهای برنامه‌نویسی چندلایه در پروژه‌های کوچک بیشتر از اینکه کار ما را ساده کنند دست‌وپاگیر به نظر برسند ولی با بالا رفتن حجم کدها و بزرگ شدن اسکیل پروژه قطعاً استفاده از چنین الگوهایی به یک ضرورت تبدیل می‌شوند. شناسایی مشکلات در این روش بسیار آسان‌تر است. فرایند دیباگ و اشکال‌زدایی برنامه برای هر لایه مجزا است و نیازی نیست با بروز هر مشکل تمامی کدها را بررسی کنیم. با جستجوی کلمۀ MVP Sample Project در اینترنت و گیت‌هاب به مثال‌های متعددی دسترسی خواهید داشت. توصیه می‌کنیم با انجام این پروژه‌ها مهارت خود را در برنامه‌نویسی چندلایه افزایش دهید. کدهای این پروژه را می‌توانید از مخزن گیت‌هاب دریافت کنید.

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

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

0 دیدگاه

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