معماری MVVM چیست؟
معماری MVVM که مخفف Model-View-Viewmodel است یکی از الگو های معماری نرم افزار است. MVVM با ایجاد یک منطق کسب و کار یا منطق عقب پایان (مدل داده)، تفکیک توسعه ی رابط کاربر گرافیکی را آسان می کند. مدل View درMVVM یک مبدل ارزش است به این معنی که مدل View دید مسئول انعکاس اشیا داده از مدل است به طوری که اشیا به راحتی مدیریت و ارائه شوند. MVVM توسط معماران مایکروسافت به طور خاص برای برنامه نویسی رویدادها از طریق رابط کاربری ساده طراحی شده است. با ما همراه باشید تا شما را درباره پیاده سازی معماری MVVM ، برنامه نویسی MVVM در اندرید به همراه یک مثال کاربردی آشنا کنیم. | { مرجع آموزش برنامه نویسی اندروید الکامکو }
منطق در معماری MVVM :
معماری MVVM برای استفاده از توابع اتصال پذیری داده ها در WPF (اساس نمایش ویندوز) طراحی شده است. به جای نیاز به توسعه دهندگان تجربه (UX) برای نوشتن کد GUI، می توانید از زبان نشانه گذاری چارچوب استفاده کنید و اتصال داده ها به مدل که توسط توسعه دهندگان برنامه نوشته شده و نگهداری می شود را مشاهده کنید. این الگوی معماری تلاش می کند تا هر دو مزایای تفکیک توسعه عملکردی ارائه شده توسط MVC را به دست آورد.
انتقاد در معماری MVVM :
انتقاد از این الگو به خودی خود توسط سازنده ان مطرح شده، وجود سربار در اجرای overkill برای عملیات UI ساده در این معماری است. به همین دلیل برای برنامه های بزرگ تر، تعمیم دادن ViewModel سخت تر می شود و گاهی نیز غیر ممکن می گردد. علاوه بر این با استفاده از این مدل معماری در برنامه های بزرگ، اتصال داده ها می تواند به مصرف حافظه قابل توجهی منجر شود و حافظه زیادی را اشغال کند.
پیاده سازی معماری MVVM :
از معماری MVVM در پیاده سازی برنامه ها و فریم ورک های مختلفی استفاده شده است. از جمله مهم ترین فریم ورک های NET که از این معماری برای پیاده سازی استفاده کرده اند می توان به DOTVVM اشاره کرد. همچنین این معماری در فریم ورک های جاوا اسکریپتی مطرح مثل Vue.js و Oracle JET نیز مورد استفاده قرار گرفته است.
هدف از کاربرد معماری MVVM در اندروید چیست؟
با یکسری کلاس های ViewModel پیچیدگی view رو از model و پیچیدگی model از view رو مخفی می کند، در یک کلاس اکتیویتی ما داده را مستقیما دستکاری نکنیم و همچنین با تغییر داده ها مستقیما کامپوننتی رو تغییر ندهیم، خب اینکه کارمان را زیاد می کند و حجم کار بالا میرود! بله درسته، ولی تست هر بخش به تنهایی و جلوگیری از ایرادهای ناخواسته (مثل تغییر وضعیت lifecycle که به اجبار یه فرگمنت یا اکتیویتی رو destroy می کند و باعث از دست رفتن اطلاعات میشه) رو راحت تر میکند.
یادآوری معماری MVP :
فرض کنید میخواهیم در اپ یک activity داشته باشیم که لیست فیلم های در حال اکران رو از بک-اند بگیره و نمایش دهد. اسم این activity رو MovieActivity میزاریم . در معماری MVP ما ۲ تا اینترفیس باید داشته باشیم یکی برای View و یکی هم برایPresenter .
اسم آنها را به ترتیب MovieView و MoviePresenter میزاریم . یک کلاس هم باید داشته باشیم که اینترفیس Presenter رو پیاده سازی(Implement) کنه اسم این کلاس را هم MoviePresenterImpl می گذاریم. در شکل زیر استفاده از معماریMVP برایMovieActivity نشان داده شده است.
همانطور که در شکل مشخص است کلاس MoviePresenterImpl از اینترفیس MovieView برای ارتباط با activity استفاده میکند . یکی از عیب های مهم معماری MVP این است که Presenter ارتباط تنگاتنگی با View دارد و اون طور که باید مستقل نیست تا بتوان به راحتی براش تست نوشت.
استفاده از معماری MVVM در اندروید به جای معماری MVP در اندروید
برای برطرف کردن مشکلی که در بخش قبل عنوان کردیم اندروید معماری جدیدی را تحت عنوان MVVM معرفی کرده است. این حروف مخفف Model View ViewModel هستند. برای پیاده سازی این معماری اندروید دو تا کامپوننت به نامهای LiveData و ViewModel را معرفی کرده است.
پیاده سازی معماری MVVM با استفاده از ViewModel و LiveData نه تنها باعث می شود مشکل ارتباط تنگاتنگ Presenter و View که در MVP وجود داشت برطرف شود، بلکه باعث می شود دیتایی که اکتیویتی بهش نیاز دارد به چرخه زندگی اکتیویتی وابسته نباشد و با از بین رفتن اکتیویتی از بین نرود.
به عبارت دیگر وقتی برای اولین بار اکتیویتی ساخته می شود و دیتای مورد نیازش از بک-اند گرفته می شود این دیتا تا زمانی که اپلیکیشن بسته نشده کش می شود و برای مثال اگه کاربر گوشی رو بچرخونه و اپ در حالت افقی نمایش داده بشود از همون دیتایی که قبلا گرفته شده استفاده می شود و دیتای جدیدی از بک-اند گرفته نمی شود.
پیاده سازی MVVM در اندروید با استفاده ازViewModel و LiveData
برای استفاده از ViewModel وLiveData در اپ باید ابتدا آخرین ورژن android.arch.lifecycle:extensions را به گریدل پروژه اضافه کنید.
در معماریMVP ما ۲ تا اینترفیس داشتیم با ۲ تا کلاس ولی در معماری MVVM فقط ۲ تا کلاس داریم که ارتباط تنگاتنگی هم با هم ندارند و راحتتر میتوان برایشان تست نوشت. برای استفاده از کلاس MovieViewModel داخل اکتیویتی ابتدا باید یک نمونه از کلاس MovieViewModel درست کنید.
برای اینکار از ViewModelProviders استفاده کنید. این کلاس یه سری متدهای static دارد که از آنها میتوان برای ایجاد ViewModel ها استفاده کرد. این کلاس خودش هندل می کند که اگر قبلا نمونه ای از ViewModel وجود داشته باشد همان را برمیگرداند وگرنه یک نمونه جدید ازش درست می کند.
آموزش معماری MVVM در اندروید همراه با پیاده سازی معماری MVVM اندروید با یک مثال ساده
در ابتدا برای آموزش معماری MVVM در اندروید یک پروژه جدید در اندروید اسنودیو ایجاد می کنیم. من اسم پروژه رو SimpleMVVM گذاشتم. توجه کنید که این فقط یک مثال ساده است واسه اینکه شما با معماری MVVM در اندروید آشنا شوید. فایل color.xml و گردل مربوط به ماژول اپ رو به صورت زیر تغییر می دهیم:
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#2962FF</color> <color name="colorPrimaryDark">#0D47A1</color> <color name="colorAccent">#D81B60</color> <color name="white">#FFFFFF</color> </resources>
apply plugin: 'com.android.application' android { compileSdkVersion 28 defaultConfig { applicationId "com.elecomco.simplemvvm" minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } dataBinding { enabled = true } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' }
سه پکیج با نام های commands ، model و viewmodel ایجاد کرده، در پکیج model که در اینجا همان لایه مدل ما می باشد، یک کلاس جدید با نام User ایجاد می کنیم. این کلاس که دیتا مدل ما می باشد را به صورت زیر کدنویسی میکنیم:
package com.elecomco.simplemvvm.model; public class User { private String email; private String password; public String emailhint; public String passwordhint; public User() { } public User(String emailhint, String passwordhint) { this.emailhint = emailhint; this.passwordhint = passwordhint; } }
حالا نوبت به ساخت لایه ویو مدل می باشد. روی پکیج viewmodel راست کلیک کرده و یک کلاس جدید با نام UserModel ایجاد می کنیم. این کلاس یک مدل از همان کلاس User است که خودمان ایجاد کردیم. در اینجا نیز از همان فیلد های کلاس قبلی استفاده می کنیم. دقت کنید که این بار آنها را از نوع private در نظر می گیریم. ورودی سازنده این کلاس یک آبجکت از کلاس User است. کد های نهایی این کلاس به صورت زیر است:
package com.elecomco.simplemvvm.viewmodel; import android.databinding.BaseObservable; import com.elecomco.simplemvvm.R; import com.elecomco.simplemvvm.model.User; public class UserModel extends BaseObservable { private String email; private String password; private String emailhint; private String passwordhint; public UserModel(User user) { this.emailhint=user.emailhint; this.passwordhint=user.passwordhint; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; notifyPropertyChanged(R.id.emailid); } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; notifyPropertyChanged(R.id.passid); } public String getEmailhint() { return emailhint; } public void setEmailhint(String emailhint) { this.emailhint = emailhint; } public String getPasswordhint() { return passwordhint; } public void setPasswordhint(String passwordhint) { this.passwordhint = passwordhint; } }
حالا یک اینترفیس (Interface) با نام UserLogin درون پکیج commands ایجاد کرده و درون آن یک تابع با نام onClickLogin ایجاد می کنیم. این تابع به صورت Proto Type تعریف می شود و در جایی که میخواهیم از آن استفاده کنیم، بدنه اش ساخته می شود. کدها به صورت زیر میباشد:
package com.elecomco.simplemvvm.commands; public interface UserLogin { void onClickLogin(); }
اکنون لی اوت activity_main.xml را به صورت زیر کدنویسی می کنیم:
<?xml version="1.0" encoding="utf-8"?> <layout 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"> <data> <variable name="login" type="com.elecomco.simplemvvm.viewmodel.UserModel" /> <variable name="userloginevent" type="com.elecomco.simplemvvm.commands.UserLogin" /> </data> <LinearLayout android:gravity="center" android:background="@color/colorPrimary" android:orientation="vertical" android:padding="5dp" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <EditText android:id="@+id/emailid" android:text="@={login.email}" android:layout_marginTop="10dp" android:textColor="@color/white" android:textColorHint="@color/white" android:hint="@{login.emailhint}" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/passid" android:text="@={login.password}" android:layout_marginTop="10dp" android:textColor="@color/white" android:textColorHint="@color/white" android:hint="@{login.passwordhint}" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:text="Login" android:textColor="@color/white" android:textStyle="bold" android:layout_marginTop="10dp" android:background="@color/colorAccent" android:onClick="@{(v)->userloginevent.onClickLogin()}" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> </layout>
همانطور که در کدهای برنامه نویسی MVVM بالا می بینید، ما یک تگ data ایجاد کرده ایم که در آن از اینترفیس UserLogin استفاده کرده ایم که رویداد مربوط به لاگین کردن از طریق آن انجام می شود.
همچنین در یکی از variable ها از UserModel استفاده شده است. در واقع در اینجا به جای view از لایه ViewModel استفاده کردیم. این لایه کنترل کاملی روی ویو داشته و آن را مدیریت می کند. این شکلی از DataBinding میباشد که می خواهیم توسط معماری MVVM دیتا را ارسال و دریافت کنیم.
در رویداد onClick دکمه (Button) ، ایمیل و پسورد که دیتاهای مورد نظر ما می باشند، توسط رویداد مربوطه کنترل شده و در نهایت در صورت صحیح بودن ارسال می شوند (یا اصلاحا Bind می شوند). حالا نوبت به اکتیویتی می رسد که آنرا به صورت زیر کدنویسی کرده و در پایان توضیحات مربوطه را ارئه می دهم:
package com.elecomco.simplemvvm; import android.databinding.DataBindingUtil; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.Toast; import com.elecomco.simplemvvm.commands.UserLogin; import com.elecomco.simplemvvm.databinding.ActivityMainBinding; import com.elecomco.simplemvvm.model.User; import com.elecomco.simplemvvm.viewmodel.UserModel; public class MainActivity extends AppCompatActivity { private ActivityMainBinding activityMainBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.activity_main); activityMainBinding= DataBindingUtil.setContentView(this,R.layout.activity_main); UserModel userModel=new UserModel(new User("Email","Password")); activityMainBinding.setLogin(userModel); activityMainBinding.setUserloginevent(new UserLogin() { @Override public void onClickLogin() { Toast.makeText(MainActivity.this, "username = "+activityMainBinding.getLogin().getEmail(), Toast.LENGTH_SHORT).show(); } }); } }
در این آموزش معماری MVVM در اندروید می بینید که از کلاس ActivityMainBinding استفاده کرده ایم و آنرا جایگزین اکتیویتی کرده ایم به این خاطر که از DataBinding برای ارسال و دریافت داده ها استفاده کرده ایم. در سازنده کلاس UserModel نیز از لایه مدل استفاده شده که در همین جا آنرا مقدار دهی می کنیم. همان طور که مشاهده می کنید در اینجا یک چرخه ارتباطی بین لایه های مدل ، ویو مدل و ویو ایجاد شده است.
این فقط یک مثال ساده از برنامه نویسی MVVM برای آشنایی شما با نحوه کار با الگوی طراحی MVVM یا همان (Design Pattern MVVM) پترن دیزاین MVVM بود. با امید به اینکه این مقاله مورد توجه و رضایت شما عزیزان قرار گرفته باشد. | مقالات آموزشی الکامکو