معماری های مختلف در توسعه اپلیکیشن های اندرویدی
معماری های مختلفی برای توسعه برنامه های اندروید مورد استفاده قرار می گیرند، از جمله MVP (Model View Presenter)، MVVM (Model View ViewModel)، Clean Architecture، و Dependency Injection.
هر کدام از این معماری ها دارای ویژگی ها و مزایای خاصی هستند که به توسعه دهندگان کمک می کنند تا برنامه های پایدار، قابل توسعه و قابل نگهداری بسازند. هر معماری نیاز به مهارت های مشخصی دارد و به دلیل دشواری هر معماری، پیشنهاد می شود تا تازه واردان، با معماری ساده MVC شروع به کار کنند و سپس با توسعه تجربه و مهارت خود، معماری های پیشرفته تر را فراگیرند.
معماری MVC:
معماری MVC یک معماری بسیار مفید و قدرتمند برای توسعه برنامه های اندروید است که به شما کمک می کند کد خود را به شکلی سازماندهی شده و با قابلیت توسعه بیشتری داشته باشید. با رعایت این معماری، شما می توانید کدهایتان را به راحتی مدیریت کنید و هرگونه تغییری در برنامه خود را با اطمینان و سهولت انجام دهید.
معماری MVC یا Model-View-Controller یک معماری متداول برای توسعه نرم افزار است. در این معماری، کدهای برنامه به سه قسمت تقسیم می شوند: مدل (Model)، نمایش (View) و کنترل کننده (Controller).
برای فهم بهتر این معماری، می توانیم یک مثال ساده را در نظر بگیریم. فرض کنید که شما می خواهید یک برنامه ساده تحت وب برای نمایش لیستی از کاربران بنویسید.
در معماری MVC، شما باید این برنامه را به سه قسمت تقسیم کنید:
1- مدل (Model): این بخش مسئول اطلاعات است. در اینجا شما باید اطلاعات مورد نیاز برنامه را تعریف کنید، مثلاً لیست کاربران. برای نمونه، شما می توانید یک کلاس ساده برای کاربران ایجاد کنید که شامل نام، نام خانوادگی و سن کاربران باشد.
2- نمایش (View): این بخش مسئول نمایش اطلاعات است. در اینجا شما باید یک واسط کاربری برای نمایش لیست کاربران طراحی کنید. مثلاً شما می توانید یک لیست با عنوان کاربران ایجاد کنید که لیستی از نام های کاربران را به کاربر نمایش دهد.
3- کنترل کننده (Controller): این بخش مسئول کنترل جریان کار است. در اینجا شما باید روش هایی برای تعامل با کاربر و مدیریت کاربران را پیاده سازی کنید. مثلاً شما می توانید یک کلاس برای کنترل کاربران ایجاد کنید که متدهایی برای اضافه کردن، حذف و ویرایش کاربران را داشته باشد.
با تقسیم کردن برنامه به سه قسمت Model، View و Controller، شما می توانید به راحتی کد برنامه خود را مدیریت کنید.
در این مثال، برنامه ای تحت اندروید است که یک لیست از کاربران را نمایش می دهد. برای این کار، از معماری MVC استفاده خواهیم کرد.
ابتدا، مدل را تعریف می کنیم. در اینجا، مدل ما شامل یک کلاس ساده با نام User است که شامل نام و سن کاربر می باشد:
public class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } }
سپس، نمایش را تعریف می کنیم. در اینجا، ما یک فایل layout با نام activity_main.xml ایجاد می کنیم که شامل یک ListView است:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
در نهایت، کنترل کننده را پیاده سازی می کنیم. در اینجا، کنترل کننده ما یک کلاس با نام MainActivity است که شامل یک ArrayList از کاربران و یک ArrayAdapter برای پر کردن لیست است:
public class MainActivity extends AppCompatActivity { private ListView listView; private ArrayAdapter<User> adapter; private ArrayList<User> users = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Find the ListView listView = findViewById(R.id.list_view); // Create some sample users users.add(new User("John Doe", 25)); users.add(new User("Jane Smith", 30)); users.add(new User("Bob Johnson", 40)); // Create the adapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, users); // Set the adapter to the ListView listView.setAdapter(adapter); } }
در این مثال، ما ابتدا ListView را پیدا کرده و سپس چند کاربر نمونه را ایجاد کرده و آن ها را به ArrayList اضافه کرده ایم.
معماری MVVM:
استفاده از معماری MVVM نسبت به MVC دارای مزایای زیادی است که به صورت خلاصه در زیر آورده شدهاند:
۱- جداسازی مسئولیت: در معماری MVVM، ViewModel مسئولیت ارتباط بین View و Model را برعهده دارد و به همین دلیل کدهای آن دو به خوبی جدا شده و قابل تست میباشد.
۲- قابلیت تست بالا: ViewModel و View با استفاده از LiveData به خوبی جدا شدهاند که به برنامه نویسان اجازه میدهد تا ViewModel را به آسانی تست کنند.
۳- کد قابل خواندنتر: ViewModel مسئولیت دادههایی که به View ارسال میشود را دارد ولی View به این اطلاعات نیازی ندارد. این باعث میشود کد ViewModel سادهتر و خواناتر باشد.
۴- کاهش برخورداری از Callback: به دلیل اینکه ViewModel از LiveData به عنوان مکانیزم ارتباطی استفاده میکند، تعداد برخورداری از Callback در این معماری کاهش مییابد و در نتیجه احتمال بروز خطا کمتر میشود.
۵- پایداری بیشتر: استفاده از LiveData باعث میشود که ViewModel و View همیشه به هم متصل بمانند، بنابراین در صورتی که دادهها تغییر کنند، این تغییر به صورت خودکار در View نمایش داده میشود و کد پایدارتر میشود.
با توجه به این مزایای ذکر شده، استفاده از معماری MVVM در بسیاری از موارد بهینهتر و مناسبتر از MVC به نظر میرسد.
میتوانیم همان مثال قبلی را در قالب معماری MVVM پیادهسازی کنیم. در معماری MVVM، ViewModel مسئولیت اتصال View و Model را بر عهده دارد و View به ViewModel بایند میشود.
در اینجا، میتوانیم کلاس مدل را به عنوان مدل پروژه، ViewModel را به عنوان ارتباط بین View و Model و Activity را به عنوان View پیادهسازی کنیم. در MVVM، ViewModel با استفاده از LiveData، مقادیر را به View منتقل میکند و تغییرات را به Model اعمال میکند.
public class Project { private String name; private String description; public Project(String name, String description) { this.name = name; this.description = description; } public String getName() { return name; } public String getDescription() { return description; } } public class ProjectViewModel extends ViewModel { private MutableLiveData<Project> projectLiveData; public ProjectViewModel() { projectLiveData = new MutableLiveData<>(); } public LiveData<Project> getProjectLiveData() { return projectLiveData; } public void setProject(Project project) { projectLiveData.setValue(project); } } public class MainActivity extends AppCompatActivity { private TextView nameTextView; private TextView descriptionTextView; private Button updateButton; private ProjectViewModel projectViewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); nameTextView = findViewById(R.id.nameTextView); descriptionTextView = findViewById(R.id.descriptionTextView); updateButton = findViewById(R.id.updateButton); projectViewModel = new ViewModelProvider(this).get(ProjectViewModel.class); Project project = new Project("Sample Project", "This is a sample project."); projectViewModel.setProject(project); projectViewModel.getProjectLiveData().observe(this, new Observer<Project>() { @Override public void onChanged(Project project) { nameTextView.setText(project.getName()); descriptionTextView.setText(project.getDescription()); } }); updateButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Project newProject = new Project("Updated Project", "This project has been updated."); projectViewModel.setProject(newProject); } }); } }
در این مثال، ما یک ViewModel به نام ProjectViewModel
ساختهایم که یک MutableLiveData
از نوع Project
را نگه میدارد. ViewModel با استفاده از setValue()
مقادیر را به LiveData منتقل میکند و View به LiveData بایند میشود. همچنین، ما یک Observer را به LiveData<Project>
اضافه کردهایم تا زمانی که مقدار LiveData تغییر کرد، View به روزرسانی شود.
در کلاس MainActivity
، ما ViewModel را با استفاده از ViewModelProvider
ساخته و یک شی از ProjectViewModel
دریافت میکنیم. پس از ساخت ViewModel، یک شی از Project
با نام "Sample Project" و توضیحات "This is a sample project." ساخته و با استفاده از setProject()
به ViewModel اضافه میکنیم.
سپس با استفاده از observe()
، ما یک Observer برای LiveData ایجاد کردهایم. هر زمان که LiveData تغییر کرد، متد onChanged()
این Observer فراخوانی میشود و مقدار جدید را به View منتقل میکند.
در نهایت، ما یک Listener برای دکمه updateButton اضافه کردهایم. هر زمان که کاربر روی این دکمه کلیک کرد، یک شی از Project
با نام "Updated Project" و توضیحات "This project has been updated." ساخته و با استفاده از setProject()
به ViewModel اضافه میکنیم که سبب به روزرسانی View شود.
با این حال، برای استفاده از معماری MVVM در اندروید استدیو، به نیاز به بررسی بیشتری از مفاهیم ViewModel و LiveData و نحوه استفاده از آنها دارید.
معماری MVP:
معماری MVP یکی دیگر از معماریهای محبوب برای برنامهنویسی اندروید و جاوا است که شباهتهایی به معماری MVC دارد. در این معماری، برای جداسازی مسئولیتها، View به دو بخش View و Presenter تقسیم میشود. Presenter مسئولیت کنترل کردن وضعیت برنامه و ارتباط با Model را برعهده دارد و از طریق View به کاربر ارتباط برقرار میکند. در این معماری، View فقط وظیفه نمایش دادن دادهها و برقراری ارتباط با Presenter را دارد.
برای نمایش کاربری در این معماری، میتوانیم از XML استفاده کنیم. در زیر، یک مثال ساده از معماری MVP در اندروید با استفاده از XML آمده است:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/text_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Hello MVP!" /> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Change Text" /> </LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements MainContract.View { private TextView textView; private Button button; private MainPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.text_view); button = findViewById(R.id.button); presenter = new MainPresenter(this); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { presenter.onButtonClicked(); } }); } @Override public void setText(String text) { textView.setText(text); } }
MainContract.java
public interface MainContract { interface View { void setText(String text); } interface Presenter { void onButtonClicked(); } }
MainPresenter.java
public class MainPresenter implements MainContract.Presenter { private MainContract.View view; public MainPresenter(MainContract.View view) { this.view = view; } @Override public void onButtonClicked() { view.setText("Text Changed!"); } }
در معماری MVP، Presenter مسئولیت ارتباط با Model را دارد و برای این کار از یک متد در Model استفاده میکند. همچنین Presenter باید بتواند به View دستوراتی مانند بروزرسانی یا نمایش پیامهای خطا را بدهد. به همین دلیل، Presenter باید یک رابط برای ارتباط با View تعریف کند. در مثال بالا، رابط MainContract
به عنوان رابط ارتباطی بین Presenter و View استفاده شده است.
Presenter دو متد دارد: onSendButtonClick()
و onMessageReceived()
.
onSendButtonClick()
به دکمه Send کلیک شده استفاده میشود. این متد با استفاده از متدsendMessage()
در Model شماره تلفن را به آن ارسال میکند.onMessageReceived()
پیامی را که از Model دریافت شده، به View ارسال میکند تا بروزرسانی شود.
نکته: در این مثال، Model شامل یک کلاس ساده است که فقط یک متد sendMessage()
را دارد و با توجه به شماره تلفنی که به آن داده میشود، یک پیامی تولید میکند. البته، در پروژههای واقعی Model شامل تمام منطق برنامه است و میتواند از پایگاه داده، سرویسهای وب یا سایر منابع دیگر استفاده کند.
در مجموع، MVP مزایای زیادی دارد، از جمله افزایش قابلیت تست، کاهش وابستگی بین لایهها و تسهیل در توسعه و نگهداری برنامه.
در معماری MVP، View مسئولیتهای خود را انجام میدهد و از Presenter برای ارتباط با Model استفاده میکند. در واقع، Presenter بین View و Model واسط میشود و با استفاده از رابطی که برای View تعریف شده، دادههایی را که از Model دریافت میکند به View ارسال میکند و وظایفی مانند بروزرسانی ویو را برعهده دارد.
در مثال بالا، یک View ایجاد شده است که شامل یک EditText برای ورودی شماره تلفن و یک Button برای ارسال آن به Presenter است. Presenter شامل دو متد است: یک متد برای ارسال شماره تلفن به Model و یک متد دیگر برای دریافت پاسخ از Model و بروزرسانی ویو. Model شامل یک متد است که شماره تلفن را دریافت کرده و یک رشته پیام را برای برگشت به Presenter ایجاد میکند.
مزیت اصلی MVP این است که View و Presenter مستقل از یکدیگر هستند و میتوانند به صورت جداگانه تست شوند. همچنین، این معماری به کاهش وابستگی بین View و Model کمک میکند و قابلیت توسعه و افزایش پذیری برنامه را بهبود میبخشد.