معماری MVC در اندروید

معرفی کامل معماری MVC اندروید با مثال کاربردی

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

معماری MVC اندروید

در مورد معرفی دیزاین پترن های معروف اندروید در یک مقاله جداگانه صحبت کردیم و گفتیم انتخاب معماری درست برای اپلیکیشن بر اساس شرایط هر پروژه بصورت جداگانه انجام میشود. اما در این مقاله درباره معرفی معماری MVC، ساختار آن، مزایا و معایب این معماری و یک مثال کاربردی از پیاده سازی آن صحبت کردیم. فهرست تیتر ها را میتوانید در باکس زیر ببینید.


معرفی معماری MVC اندروید

معرفی معماری MVC

در دنیای نرم افزارهای امروز، رابط کاربری تغییرات بیشتری نسبت به لاجیک اپلیکیشن دارد. برای همین یکی از راه حل های خوب برای ساخت نرم افزار این است که لایه های رابط کاربری و لایه منطقی نرم افزار را از هم جدا کنیم. یکی از روش های انجام این کار، معماری MVC است. استفاده از این راه حل میتواند نگهداری از اپلیکیشن و اضافه کردن ویژگی های جدید به آن را راحت تر کند.

یکی از دیزاین پترن های بسیار معروفی که در برنامه نویسی اندروید به کار میرود، معماری MVC (مخفف Model-View-Controller) است. این معماری یکی از قدیمی ترین الگوهای طراحی اندروید میباشد و پروژه ما را به سه قسمت تقسیم میکند که به هر کدام از این قسمت ها یک لایه میگوییم. بیایید اول با این لایه ها آشنا شویم و بعد سراغ پیاده سازی آنها برویم. لایه های معماری MVC این ها هستند:

  • لایه Model: این لایه مسئول سر و کله زدن با همه داده های اپلیکیشن است. علاوه بر داده ها همه منطق اپلیکیشن هم در این لایه پیاده سازی میشود. داده های اپلیکیشن از راه های مختلفی تامین میشوند، میتوانند از یک فایل، از دیتابیس یا از سرور ها بیایند. ارتباط اپلیکیشن با همه این منابع داده ای باید در این لایه پیاده سازی شوند.
  • لایه View: این لایه همه اجزایی که به کاربر نمایش داده میشوند یا همان رابط کاربری را درون خودش دارد. اما یکی از وظیفه های اصلی این لایه، نمایش داده هایی است که در لایه Model ذخیره شده اند. یعنی تا وقتی که اطلاعات مختلف که از لایه Model می آیند در رابط کاربری نمایش داده نشوند، کاربر از وجود آنها با خبر نخواهد شد.
  • لایه Controller: این لایه رابطه بین لایه های  Model و View را برقرار میکند. در معماری MVC، لایه Controller در حقیقت منطق مرکزی اپلیکیشن ما را درون خودش دارد. روش کار لایه Controller به این صورت است که اکشن های کاربر را از لایه View دریافت میکند، تصمیم میگیرد چه اتفاقی در اپلیکیشن رخ بدهد و بعد از آن لایه Model را به روز رسانی میکند.
معماری MVC

به عکس بالا دقت کنید. کل معماری MVC به این شکل کار میکند: لایه View جایی است که با کاربر و سر و کله میزند. یعنی المان های مختلف و داده ها را به کاربر نشان میدهد و اکشن های کاربر را دریافت میکند. بعد از دریافت اکشن های کاربر، لایه View باید لایه Controller را با خبر کند. لایه Controller تصمیم میگیرد که چه کارهایی در اپلیکیشن انجام شود و به لایه Model خبر میدهد که داده های مورد نیاز را ذخیره کند. پس لایه Model داده های به روز رسانی شده را به اطلاع لایه View میرساند و تغییرات داده ها روی UI نمایش داده خواهند شد.

نکته: دقت کنید که در سناریوی کار معماری MVC، به روز رسانی های داده مستقیما توسط لایه Model به لایه View منتقل میشوند و لایه Controller در این ارتباط نقشی ندارد. در حقیقت یکی از اشکالاتی که به معماری MVC گرفته میشود، این است که لایه Model با دو لایه دیگر در ارتباط است. در صورتی که بهترین حالت این است که لایه Model فقط با یک لایه ارتباط داشته باشد.

یکی از قوانین مهم در دنیای معماری های اندروید، قانون مسئولیت تکی یا Single Responsibility Principle است. این قاعده بصورت ساده اینطور میگوید که هر لایه باید فقط و فقط یک مسئولیت تنها داشته باشد. طبق این قانون مثلا لایه View تنها کاری که باید انجام بدهد آپدیت کردن لایه Controller برای همه اکشن های کاربر و نشان دادن داده هایی است که از لایه Model می آیند. پس این لایه نباید هیچ کدام از منطق های اپلیکیشن را درون خودش داشته باشد.


معماری MVC در اندروید

معماری mvc در اندروید

حوالی سال 2011 وقتی که سیستم عامل اندروید شروع به معروف تر شدن کرد، درخواست ها برای معماری های نرم افزاری افزایش پیدا کرد. از آنجایی که معماری MVC یکی از مشهور ترین معماری های آن زمان بود، توسعه دهنده ها سعی کردند این معماری را برای سیستم عامل اندروید هم پیاده سازی کنند.

اگر داخل وبسایت StackOverFlow درباره اینکه “چگونه میتوانیم معماری MVC را در اندروید پیاده سازی کنیم؟” سرچ کنید، یکی از مشهور ترین پاسخ ها این است که در اندروید، اکتیویتی میتواند هم لایه View و هم لایه Controller باشد. شاید این ایده مقداری اشتباه به نظر بیاید، اما در آن زمان، همه تاکید بر این بود که لایه Model تست پذیر باشد و معمولا انتخاب ها برای لایه های View و Controller به پلتفرم بستگی داشت.


مزایای معماری MVC

مزایا

دیزاین پترن Model-View-Controller میتواند پروژه ما را به قسمت های مختلف تقسیم کند. این مسئله باعث بالا رفتن امکان تست پذیری کد و راحت تر شدن توسعه های اپلیکیشن در آینده میشود و به راحتی میتوان ویژگی های جدید به اپلیکیشن اضافه کرد.

کلاس های لایه Model هیچ ارجاعی به کلاس های Android نگهداری نمیکنند و به همین دلیل میتوانند بصورت مستقیم تست شوند. کلاس های درون لایه Controller هم هیچ کدام از کلاس های Android را Implement یا Extend نمیکنند. اما باید ارجاع هایی به اینترفیس های لایه View درون آن وجود داشته باشد. با استفاده از این روش، تست کردن لایه Controller هم امکان پذیر میشود.


معایب معماری MVC

معایب

به عنوان یکی از معماری های معروف در اندروید، معماری MVC دارای ایراداتی هم میباشد. تعدادی از معایب این معماری را با هم بررسی میکنیم:


مشکل اول: لایه View به لایه های Controller و Model وابستگی دارد

وابستگی لایه View به لایه Model یک نکته ضعف در View های پیچیده است. برای به حداقل رساندن منطق در لایه View، لایه Model باید برای هر المانی که قرار است نمایش داده شود، متدهای تست پذیر ارائه بدهد. در مدل Active معماری MVC، این مسئله باعث افزایش تصاعدی تعداد کلاس ها و متد ها میشود. زیرا برای هر نوع داده، Observer های متفاوتی مورد نیاز خواهد بود.

علاوه بر اینها، چون لایه View به هر دو لایه Model و Contoller وابستگی دارد، اگر بخواهیم منطق های مربوط به رابط کاربری را تغییر بدهیم شاید نیاز باشد متدها و کلاس های زیادی را ویرایش کنید. این مشکل باعث پایین آمدن انعطاف پذیری این معماری میشود.


مشکل دوم: چه کسی منطق UI را مدیریت میکند؟

بر اساس معماری MVC، لایه Controller باید لایه Model را آپدیت کند و لایه View باید داده هایی که قرار است نمایش داده شوند را از لایه Model دریافت کند. اما چه کسی تصمیم میگیرد که داده ها چگونه نشان داده شوند؟ این مثال را در نظر بگیرید: یک کاربر داریم که شامل اطلاعاتی مانند نام و نام خانوادگی است. در لایه View میخواهیم نام کاربر را به شکل “نام، نام خانوادگی” نشان بدهیم (مثلا “علی، خانقلی”).

اگر نقش لایه Model فقط تامین داده های خام باشد، پس کدهای لایه View باید به این صورت باشند:

String firstName = userModel.getFirstName();
String lastName = userModel.getLastName();
nameTextView.setText(lastName + ", " + firstName)

بنابراین طبق این کدها، نتیجه میگیریم که مدیریت منطق UI بر عهده لایه View میباشد. اما این مسئله باعث میشود منطق UI به هیچ وجه تست پذیر نباشد.

راه حل دیگر این است که لایه Model فقط داده هایی که نیاز است نمایش داده شود را ارائه بدهد و همه منطق های بیزینسی را از لایه View پنهان کند. اما اگر این اتفاق رخ بدهد، در حقیقت لایه Model مسئول مدیریت منطق های UI و بیزینسی میشوند. این مدل راه حل تست پذیر است اما باعث میشود لایه Model به لایه View وابسته شود.

String name = userModel.getDisplayName(); nameTextView.setText(name);

انواع مختلف معماری MVC

انواع مختلف معماری MVC

مدل های زیادی از معماری MVC وجود دارند. دو مدل بیشتر شناخته شده آن مدل های Passive و Active هستند. این مدل ها در نحوه اطلاع رسانی و ارتباط لایه ها با هم تفاوت دارند. در ادامه این دو مدل را با هم بررسی میکنیم:


مدل Passive

در مدل Passive از معماری MVC لایه Controller تنها لایه ای است میتواند لایه Model را دستکاری کند. این دستکاری ها بر اساس اکشن های کاربر انجام میشوند. بعد از این که لایه Model آپدیت شد، لایه Controller به لایه View خبر میدهد که این لایه هم باید آپدیت شود. در این لحظه، لایه View داده ها را از لایه Model درخواست میکند. به عکس زیر توجه کنید.

مدل passive معماری mvc

مدل Active

در سناریوهایی که لایه Controller تنها لایه ای نیست که لایه Model را تغییر میدهد، لایه Model به راهی برای اطلاع رسانی به لایه View و دیگر کلاس ها نیاز دارد. این مسئله با استفاده از الگوی Observer انجام خواهد شد. این مدل شامل تعدادی Observer است که آپدیت ها را منتقل میکنند. سپس لایه View اینترفیس Observer را Implement میکند و یک Observer را روی لایه Model ثبت نام میکند.

مدل active معماری mvc

هر بار که لایه Model آپدیت میشود، این لایه سراغ مجموعه Observer های موجود رفته و متد update همه آنها را فراخوانی میکند. پیاده سازی این متد در لایه View کاری میکند که آخرین داده ها از لایه Model دریافت شوند.

مدل active معماری mvc

تفاوت مدل های Active و Passive

تفاوت دو مدل Passive و Active در پیاده سازی لایه Model است. یعنی تعریف هردوی این مدل ها بر اساس روشی است که لایه Model طبق آن کار میکند. یعنی در مدل Active، لایه Model بصورت فعال کار میکند و هر بار که توسط لایه Controller به روز میشود، خودش لایه View را آپدیت میکند. اما در مدل Passive، روش کار لایه Model بصورت منفعل است و وقتی که توسط لایه Controller به روز میشود، خودش لایه View را آپدیت نمیکند. مسئولیت این کار در مدل Passive به عهده لایه Controller است.


معماری MVC چگونه باید در اندروید پیاده سازی شود؟

پیاده سازی معماری mvc در اندروید

امروزه پیاده سازی معماری MVC در اندروید راه حل های ساده تری نسبت به گذشته دارد. اکتیویتی ها، فرگمنت ها و رابط کاربری اپ باید در لایه View معماری MVC قرار بگیرند. لایه Controller باید کلاس جداگانه ای باشد که هیچکدام از کلاس های Android را extend نمیکند. لایه Model هم دقیقا همینطور است.

از آنجایی که لایه Controller باید به لایه View بگوید که به روز رسانی شود، یک مشکل هنگام اتصال لایه های Controller و View ممکن است به وجود بیاید. در مدل Passive از معماری MVC لایه Controller باید یک ارجاع به لایه View درون خود نگهداری کند. راحت ترین راه برای انجام این کار، داشتن یک اینترفیس به نام BaseView است که توسط اکتیویتی، فرگمنت و View باید آن را extend کنند. پس لایه Controller باید یک ارجاع به BaseView را درون خودش نگه دارد.


مثال کاربردی: اعتبار سنجی لاگین با استفاده از معماری MVC

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


گام اول: یک پروژه جدید میسازیم

اندروید استودیو را باز میکنیم و یک پروژه جدید با استفاده از یک Activity خالی میسازیم و یک نام برای آن انتخاب میکنیم.


گام دوم: 3 پکیج برای کامپوننت های معماری MVC ایجاد میکنیم

برای اینکه بخواهیم کامپوننت های معماری MVC را در اندروید پیاده سازی کنیم، باید 3 پکیج با این اسم ها در پروژه خودمان درست کنیم:

  • Model
  • View
  • Controller

کلاس ها یا اینترفیس های مربوط به هر لایه را در هر کدام از این پکیج ها خواهیم ریخت. برای ساخت پکیج جدید، روی فایل اصلی پروژه کلیک راست میکنیم، از گزینه New، گزینه Package را انتخاب میکنیم.


گام سوم: ساخت فایل های کلاس و اینترفیس ها

باید فایل های مربوط به هر لایه را در پکیج های خاص هر کدام بریزیم. در پکیج Model باید دو فایل کد درست کنیم و کدهای زیر را در آنها بریزیم:

فایل اینترفیس IUser.java

package com.example.mvcexample.Model;

public interface IUser {
String getEmail();
String getPassword();
int isValid();
}

فایل کلاس User.java

package com.example.mvcexample.Model;

import android.text.TextUtils;
import android.util.Patterns;

public class User implements IUser{
private  String email,password;
public User(String email, String password) {
    this.email = email;
    this.password = password;
}

@Override
public String getEmail() {
    return email;
}

@Override
public String getPassword() {
    return password;
}

@Override
public int isValid() {
    // 0. Check for Email Empty
    // 1. Check for Email Match pattern
    // 2. Check for Password > 6

    if(TextUtils.isEmpty(getEmail()))
        return  0;
    else if(!Patterns.EMAIL_ADDRESS.matcher(getEmail()).matches())
        return  1;
    else if(TextUtils.isEmpty(getPassword()))
        return 2;
    else if(getPassword().length()<=6)
       return 3;
    else
        return -1;

}
}

در پکیج Controller هم باید دو فایل با مشخصات زیر بسازیم:

فایل اینترفیس ILoginController.java

package com.example.mvcexample.Controller;

public interface ILoginController {
void OnLogin(String email,String Password);
}

فایل کلاس LoginController.java

package com.example.mvcexample.Controller;

import com.example.mvcexample.Model.User;
import com.example.mvcexample.View.ILoginView;

public class LoginController implements ILoginController {
ILoginView loginView;

public LoginController(ILoginView loginView) {
    this.loginView = loginView;
}

@Override
public void OnLogin(String email, String password) {
    User user = new User(email,password);
    int loginCode = user.isValid();
    if(loginCode == 0)
    {
        loginView.OnLoginError("Please enter Email");
    }else  if (loginCode == 1){
        loginView.OnLoginError("Please enter A valid Email");
    } else  if (loginCode == 2)
    {
        loginView.OnLoginError("Please enter Password");
    }else  if(loginCode == 3){
        loginView.OnLoginError("Please enter Password greater the 6 char");
    }
    else {
        loginView.OnLoginSuccess("login Successful");
    }
}
}

در پکیج View باید یک فایل بسازیم و کدهای زیر را درون آن بریزیم:

فایل اینترفیس ILoginView.java

package com.example.mvcexample.View;

public interface ILoginView {
void OnLoginSuccess(String message);
void OnLoginError(String message);
}

با ساخت این فایل ها، همه فایل های مورد نیاز معماری MVC در اندروید را درون پروژه خودمان ساخته ایم. پس میتوانیم روی ساخت یک رابط کاربری ساده مقداری بیشتر کار کنیم.

فایل MainActivity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:gravity="center">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Login Form"/>

        <EditText
            android:id="@+id/email"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:hint="Email"/>

        <EditText
            android:id="@+id/password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:hint="Password"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Login"
            android:id="@+id/loginb"/>
    </LinearLayout>

</LinearLayout>

این رابط کاربری دارای 2 عدد EditText برای ایمیل و رمز عبور، و یک دکمه برای ثبت اطلاعات است.

فایل MainActivity.java

package com.example.mvcexample;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.example.mvcexample.Controller.ILoginController;
import com.example.mvcexample.Controller.LoginController;
import com.example.mvcexample.View.ILoginView;

public class MainActivity extends AppCompatActivity  implements ILoginView {
EditText email,password;
Button loginb;
ILoginController loginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    email = (EditText) findViewById(R.id.email);
    password = (EditText)findViewById(R.id.password);

    loginb = (Button) findViewById(R.id.loginb);
    loginPresenter = new LoginController(this);

    loginb.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            loginPresenter.OnLogin(email.getText().toString().trim(),password.getText().toString().trim());
        }
    });
}

@Override
public void OnLoginSuccess(String message) {
    Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
}

@Override
public void OnLoginError(String message) {
    Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
}
}

به کدهای بالا دقت کنید. در این کدها اینترفیس ILoginView را Implement کردیم که دو متد OnLoginSuccess و OnLoginError را Override میکند و هرکدام از این متد ها یک پیام Toast را نمایش خواهند داد.

سپس یک آبجکت از کلاس LoginController ساختیم که به ما کمک میکند داده های ورودی کاربر را با کلیک کردن روی دکمه، ارسال کنیم. این کار با استفاده از متد OnLogin انجام میشود و بعد از آن اعتبار داده های ورودی کاربر سنجیده میشوند و اگر همه فیلدهایی که کاربر پر کرده است صحیح باشند یا ایراد داشته باشند، نتیجه برگردانده خواهد شد. بر اساس این نتیجه، یک پیام Toast به کاربر نمایش داده خواهد شد.


جمع بندی، سوالی دارید؟

سوالی دارید

در این نوشته درباره مفهوم معماری MVC، لایه های این معماری، مزایا و معایب این دیزاین پترن و یک مثال کاربردی از استفاده آن صحبت کردیم. اگر هر سوالی درباره این مفاهیم در ذهن شما وجود دارد یا نکته ای میتوانید به این مقاله اضافه کنید، حتما با نوشتن آن در قسمت نظرات (همین پایین) این مقاله آموزشی را کامل تر کنید.


منابع بیشتر برای مطالعه

میتوانید از منابع زیر برای مطالعه بیشتر درباره معماری MVC استفاده کنید:

درباره نویسنده

7 در مورد “معرفی کامل معماری MVC اندروید با مثال کاربردی”

  1. با سلام
    ممنون از توضیحات کاملتون
    در مثال شما من متوجه نشدم که چطوری لایه مدل مستقیما با لایه controler در ارتباط است؟

    1. سلام.
      وقتی میگیم توی این معماری لایه های کنترلر و مدل با هم در ارتباط هستن، یعنی یک سری متد ها رو توی لایه های مدل و کنترلر تعریف کردیم (هرکدوم به صورت جداگانه) و مجاز هستیم مثلا از لایه کنترلر، متد های لایه مدل رو صدا بزنیم. به این میگن ارتباط مستقیم داشتن.

دیدگاه‌ خود را بنویسید

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

اسکرول به بالا