هدف از این پست ارائه مقدمه ای بر الگویModel -View-ViewModel (MVVM) است.
الگوی MVVM گونهای از الگوی طراحی مدل ارائه شده توسطمارتین فاولر است که توسط معماران مایکروسافت کن کوپر و تد پیترز به طور خاص برای ساده کردن برنامهنویسی رویداد محور رابطهای کاربری اختراع شد. این الگو درWindows Presentation Foundation (WPF) وSilverlight گنجانده شد. جان گاسمن، یکی از معماران WPF و Silverlight مایکروسافت، MVVM را در وبلاگ خود در سال 2005 معرفی کرد.
این الگو بعدها مثل MVC به دنیای وب پا گذاشت و استفاده از اون بین فریمورکهای جاوا اسکریپت رایج شد. این الگو از سه بخش تشکیل شده است:
Model - View - ViewModel
این سه بخش، برنامهی ما رو به سه بخش اصلی تقسیم میکند. این سه بخش را میتوان به صورت جدا توسعه داد تا وابستگی بین آنها به حداقل برسد.
مدل چیزی است که از آن به عنوان شی دامنه یاد میکنیم. مدل نشان دهنده داده ها و یا اطلاعات واقعی است که ما با آن سروکار داریم. نمونهای از یک مدل ممکن است یک مخاطب (شامل نام، شماره تلفن، آدرس و غیره) باشد. در واقع مدل، اشیاء تجاری هستند که داده ها و رفتار دامنه برنامه را محصور میکنند و به سادگی داده ها را نگه می دارند.
نکته کلیدی که باید با مدل به خاطر بسپارید این است که اطلاعات را در خود نگه می دارد، اما رفتارها یا خدماتی را که اطلاعات را دستکاری می کنند، نگه نمی دارد. مسئولیتی در قبال قالب بندی متن برای زیبا به نظر رسیدن روی صفحه، یا واکشی فهرستی از آیتم ها از یک سرور راه دور نیست. منطق کسب و کار معمولاً جدا از مدل نگهداری می شود و در کلاس های دیگری که بر روی مدل عمل می کنند، محصور میشود. این همیشه درست نیست: به عنوان مثال، برخی از مدل ها ممکن است دارای اعتبار سنجی باشند.
اغلب تمیز نگه داشتن یک مدل یک چالش است. مدل یک بازنمایی واقعی از «دنیای واقعی» است. به عنوان مثال، یک رکورد تماس ممکن است حاوی آخرین تاریخ اصلاح و هویت کاربر اصلاح کننده و یک شناسه منحصر به فرد (پایگاه داده یا اطلاعات پایدار) باشد. تاریخ اصلاح شده هیچ معنای واقعی برای یک تماس در دنیای واقعی ندارد، بلکه تابعی از نحوه استفاده، ردیابی و تداوم مدل در سیستم است.
در اینجا یک مدل نمونه برای نگهداری اطلاعات تماس آمده است:
namespace MVVMExample { public class ContactModel : INotifyPropertyChanged { private string _firstName; public string FirstName { get { return _firstName; } set { _firstName = value; RaisePropertyChanged("FirstName"); RaisePropertyChanged("FullName"); } } private string _lastName; public string LastName { get { return _lastName; } set { _lastName = value; RaisePropertyChanged("LastName"); RaisePropertyChanged("FullName"); } } public string FullName { get { return string.Format("{0} {1}", FirstName, LastName); } } private string _phoneNumber; public string PhoneNumber { get { return _phoneNumber; } set { _phoneNumber = value; RaisePropertyChanged("PhoneNumber"); } } protected void RaisePropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } public event PropertyChangedEventHandler PropertyChanged; public override bool Equals(object obj) { return obj is ContactModel && ((ContactModel) obj).FullName.Equals(FullName); } public override int GetHashCode() { return FullName.GetHashCode(); } } }
نما چیزی است که اکثر ما با آن آشنا هستیم و مانند الگوهای model-view-controller (MVC) و
Model-view-presenter (MVP)، نما ساختار، طرحبندی، و ظاهر چیزی است که کاربر روی صفحه میبیند و تنها چیزی است که کاربر نهایی واقعاً با آن تعامل دارد. مثلا فرمها، دکمهها، متنها و ... . اینکه اطلاعات مدل چگونه و به چه سبکی نمایش داده شوند، وظیفه View هست. نما برای ارائه بیشتر این دادهها آزادی خاصی را می طلبد. برای مثال، یک تاریخ ممکن است به تعداد ثانیه از نیمه شب اول ژانویه 1970 (زمان یونیکس) روی مدل ذخیره شود. با این حال، به کاربر نهایی، نام ماه، تاریخ و سال در منطقه زمانی محلی آنها ارائه می شود. یک نما همچنین می تواند رفتارهای مرتبط با خود داشته باشد، مانند پذیرش ورودی کاربر. نمای ورودی (فشردن کلید، حرکات ماوس، حرکات لمسی و غیره) را مدیریت می کند که در نهایت ویژگی های مدل را دستکاری می کند. به طور خلاصه نما، داده های قالب بندی شده است که کاربر می بیند.
در MVVM، View فعال است. برخلاف یک نمای غیرفعال که هیچ دانشی از مدل ندارد و به طور کامل توسط یک کنترل کننده/ ارائه کننده دستکاری می شود، نما در MVVM شامل رفتارها، رویدادها و اتصالات داده است که در نهایت نیاز به دانش مدل و نمای مدل اساسی دارد. در حالی که این رویدادها و رفتارها ممکن است به ویژگیها، فراخوانیهای متد و دستورات نگاشت شوند، اما view همچنان مسئول رسیدگی به رویدادهای خود است و این را به طور کامل به viewmodel نمیسپارد.
نکته ای که باید در مورد نما به خاطر بسپارید این است که مسئول حفظ وضعیت خود نیست. در عوض، این را با viewmodelهمگام میکند.
در اینجا یک نمای نمونه وجود دارد که به صورت XAMLبیان شده است:
<UserControl x:Class="MVVMExample.DetailView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding CurrentContact}"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Text="Name:" HorizontalAlignment="Right" Margin="5"/> <TextBlock Text="{Binding FullName}" HorizontalAlignment="Left" Margin="5" Grid.Column="1"/> <TextBlock Text="Phone:" HorizontalAlignment="Right" Margin="5" Grid.Row="1"/> <TextBlock Text="{Binding PhoneNumber}" HorizontalAlignment="Left" Margin="5" Grid.Row="1" Grid.Column="1"/> </Grid> </UserControl>
توجه داشته باشید که اتصالات مختلف نقاط همگام سازی با viewmodel هستند.
بخش viewmodel یک انتزاع از نما است که ویژگی ها و دستورات عمومی را نشان می دهد. به جای کنترلکننده الگوی MVC یا ارائهدهنده الگوی MVP، MVVM دارای یک Binder است که ارتباط بین view و ویژگیهای محدود آن در مدل viewرا خودکار میکند.
این بخش یک بخش کلیدی از سه گانه است زیرا Presentation Separation یا مفهوم جدا نگه داشتن تفاوت های نما از مدل را معرفی میکند. به جای اینکه Model از دیدگاه کاربر نسبت به تاریخ آگاه شود، به طوری که تاریخ را به فرمت نمایش تبدیل کند، Model به سادگی داده ها را نگه می دارد، viewبه سادگی تاریخ فرمت شده را نگه می دارد و کنترل کننده به عنوان رابط بین این دو عمل می کند. کنترلر ممکن است ورودی را از viewبگیرد و آن را روی Model قرار دهد، یا ممکن است با یک سرویس تعامل داشته باشد تا Model را بازیابی کند، سپس آن را ترجمه کرده و آن را در view قرار دهد. به بیان سادهتر، viewmodel تبدیلکننده اطلاعات هست. میتواند اطلاعات را طوری به view تحویل دهد که view میخواهد. همچنین اطلاعات رو طوری به Model تحویل دهد که Model میخواهد.
viewmodel همچنین روشها، دستورات و سایر نقاطی را نشان میدهد که به حفظ وضعیت نما کمک میکند، مدل را به عنوان نتیجه اقدامات روی نما دستکاری میکند، و رویدادهایی را در خود نمای ایجاد میکند.
در اینجا یک نمونه viewmodel ممکن است به نظر برسد. ما یک کلاس BaseINPC(برای«INotifyPropertyChanged») ایجاد کردهایم که روشی برای بالا بردن رویداد تغییر ویژگی را آسان میکند.
namespace MVVMExample { public class ContactViewModel : BaseINPC { public ContactViewModel() { Contacts = new ObservableCollection<ContactModel>(); Service = new Service(); Service.GetContacts(_PopulateContacts); Delete = new DeleteCommand(Service,()=>CanDelete,contact =>{ CurrentContact = null; Service.GetContacts(_PopulateContacts); }); } private void _PopulateContacts(IEnumerable>ContactModel> contacts){ Contacts.Clear(); foreach(var contact in contacts){ Contacts.Add(contact); }} public IService Service { get; set; } public bool CanDelete{ get { return _currentContact != null; } } public ObservableCollection<ContactModel> Contacts { get; set; } public DeleteCommand Delete { get; set; } private ContactModel _currentContact; public ContactModel CurrentContact{ get { return _currentContact; } set{ _currentContact = value; RaisePropertyChanged("CurrentContact"); RaisePropertyChanged("CanDelete"); Delete.RaiseCanExecuteChanged();} } } }
این viewmodel بدیهی است که برای مدیریت لیستی از مخاطبین طراحی شده است.
همانطور که گفتیم، view و viewmodel از طریق data-binding، فراخوانی روش، ویژگی ها، رویدادها و پیام ها با هم ارتباط برقرار میکنند.
ارتباط بین بخش ظاهر برنامه و منطق برنامه توسط تکنیکی به اسم Data Binding انجام می شود. این ارتباط زنده و فعال هست. یعنی برای مثال اطلاعاتی از مدل در نما در حال نمایش هست (مثلا نام کاربری). وقتی تغییری توی اطلاعات مدل صورت میگیرد، این تغییر بطور خودکار در نما اعمال می شود و قابل دیدن هست و دیگر لازم نیست بطور دستی این کار را انجام داد.
دو نوع Data Binding وجود دارد: یک طرفه و دو طرفه
یک طرفه:
وقتی اطلاعات مدل تغییر میکند، بصورت خودکار اطلاعاتی که نما دارد نمایش میدهد و هم تغییر میکند.
دو طرفه:
توی این روش وقتی اطلاعات نمایشی در نما تغییر کند، اطلاعات مدل بصورت خودکار و آنی هم تغییر پیدا میکند.
در ViewModel نه تنها مدلها، بلکه ویژگیهای دیگر (مانند اطلاعات وضعیت و...) و دستورات را در معرض نمایش میگذارد.
View رویدادهای UI خود را مدیریت می کند، سپس آنها را از طریق دستورات به ViewModel نگاشت می کند
مدلها و ویژگیها در ViewModel از طریق Databinding دو طرفه از نما بهروزرسانی میشوند.
بخش ViewModel ممکن است مدل را مستقیماً یا خصوصیات مربوط به مدل را برای اتصال داده در معرض نمایش قرار دهد.
ViewModel میتواند دارای رابطهایی برای سرویسها، دادههای پیکربندی و غیره باشد تا ویژگیهایی را که در معرض دید قرار میدهد واکشی و دستکاری کند.
الگو MVVM یک چارچوب کامل نیست. یک الگو است و ممکن است بخشی از یک چارچوب باشد، اما تنها بخشی از راه حل کلی برای معماری برنامه شما است. به اتفاقاتی که در سرور شما میافتد یا نحوه چیدمان سرویسهای شما اهمیتی نمیدهد. این کار جداسازی نگرانیها را انجام می دهد که خوب است.
MVVM قرار نیست سرعت شما را کاهش دهد! همه الگوها و چارچوب های جدید با یک منحنی یادگیری همراه هستند. شما باید بپذیرید که توسعه دهندگان شما باید الگو را یاد بگیرند و درک کنند، اما نباید بپذیرید که کل فرآیند شما ناگهان طولانی تر شود یا به تاخیر بیفتد. این الگو زمانی مفید است که توسعه را تسریع کند، ثبات و عملکرد را بهبود بخشد، خطر را کاهش دهد و غیره. هنگامی که توسعه را کند می کند، مشکلاتی را ایجاد می کند و ممکن است بخواهید در رویکرد خود تجدید نظر کنید.
قابلیت نگهداری:
جداسازی کامل انواع مختلف کدها، ایجاد تغییرات بدون نگرانی را آسانتر کند.
این بدان معناست که می توانید چابک بمانید و به سرعت به سمت نسخه های جدید حرکت کنید.
توسعه پذیری:
گاهی اوقات با قابلیت نگهداری همپوشانی دارد، زیرا مرزهای جداسازی تمیز و کدهای دانه دانه بیشتر است.
شما شانس بیشتری برای استفاده مجدد هر یک از آن قطعات دارید.
همچنین این قابلیت را دارد که قطعات کد جدیدی را جایگزین یا اضافه کند که کارهای مشابهی را در مکان های مناسب در معماری انجام می دهند.
ارتباط شفاف:
viewmodel یک رابط شفاف برای کنترلکننده viewارائه میکند که از آن برای پر کردن لایه viewو تعامل با لایه مدل استفاده میکند که منجر به یک ارتباط شفاف بین لایههای برنامه شما میشود.
برخی از مردم فکر می کنند که برای رابط های کاربری ساده، MVVMمی تواند بیش از حد باشد.
در موارد بزرگتر، طراحی ViewModelممکن است سخت باشد.
هنگامی که پیوندهای پیچیده داده ای داریم، اشکال زدایی کمی دشوار خواهد بود.
کنترلر MVC کنترل جریان اطلاعات بین مدل و نما را به عهده دارد. درMVC یک نما میتواند بصورت مستقیم اطلاعات مدل را بخواند و نمایش دهد.
اما در الگوی MVVM مدل و نما هیچ شناختی از یکدیگر ندارند. ViewModel برخلاف کنترلر MVC، یک کنترلر نیست. بلکه نقش یک پل بین مدل و نما رو به عهده دارد. یک رابط بین اطلاعات مدل و اطلاعات نما است و توسط تکنیکی به اسم Data Binding که بطور مستقیم با هم در ارتباط هستند. توسط تکنیک Data Binding ، مدل و نما بصورت خودکار و آسان از تغییرات با خبر میشوند.
در این بخش، ما در مورد کیتهای ابزار MVVM یا چارچوبهای موجود بحث خواهیم کرد. همچنین می توانید از این فریم ورک ها استفاده کنید تا مجبور نباشید یک سری کدهای تکراری برای پیاده سازی الگوی MVVM خودتان بنویسید.
در اینجا برخی از محبوب ترین فریم ورک ها آمده است:
Prism:
Prism راهنمایی هایی را در قالب نمونه ها و مستندات ارائه می دهد که به شما کمک می کند به راحتی برنامه های دسکتاپ Windows Presentation Foundation غنی، انعطاف پذیر و با قابلیت نگهداری آسان طراحی و بسازید.
Prism از الگوهای طراحی استفاده می کند که اصول مهم طراحی معماری مانند جداسازی نگرانی ها و اتصال سست را در بر می گیرد.
Prism به شما کمک می کند تا برنامه هایی را با استفاده از مؤلفه هایی بسازید که می توانند به طور مستقل تکامل یابند، اما می توانند به راحتی و به طور یکپارچه در برنامه کلی ادغام شوند.
این نوع برنامه ها به عنوان برنامه های کاربردی ترکیبی شناخته می شوند.
MVVM Light:
MVVM Light توسط Laurent Bugnion تولید میشود و به شما کمک میکند نمای خود را از مدل خود جدا کنید، که برنامههایی را ایجاد میکند که تمیزتر و نگهداری و گسترش آن آسانتر است.
همچنین برنامه های قابل آزمایش ایجاد می کند و به شما امکان می دهد یک لایه رابط کاربری بسیار نازک تری داشته باشید (که آزمایش خودکار آن دشوارتر است).
این جعبه ابزار تأکید ویژهای بر باز کردن و ویرایش رابط کاربری در Blendدارد، از جمله ایجاد دادههای زمان طراحی برای اینکه کاربران Blendبتوانند هنگام کار با کنترلهای داده، چیزی را ببینند.
Caliburn Micro:
این یک چارچوب منبع باز کوچک دیگر است که به شما کمک می کند الگوی MVVMرا پیاده سازی کنید و همچنین از تعدادی چیزهای خارج از جعبه پشتیبانی می کند.
Caliburn Micro یک چارچوب کوچک و در عین حال قدرتمند است که برای ساخت برنامههای کاربردی در تمامی پلتفرمهای XAMLطراحی شده است.
با پشتیبانی قوی از MVVM و سایر الگوهای رابط کاربری اثبات شده، Caliburn Microشما را قادر میسازد تا راهحل خود را سریع بسازید، بدون اینکه نیازی به قربانی کردن کیفیت کد یا آزمایشپذیری باشد.
SilverlightFX:
اهداف اعلام شده برای این فریم ورک عبارتند از فعال کردن مشخصات اجمالی واسط های کاربر، امکان تفکیک آسان تر دیدگاه و کد، و ارائه یک چارچوب ناب است.
nRoute:
یکی دیگر از چارچوبهای MVVM، این کتابخانه برای «فرمانهای معکوس» منحصربهفرد است که اجازه میدهد دستورات اتصال به رویدادها در view را انجام دهد، نه اینکه view به سادگی دستورات را به viewmodel ارسال کند.
سایر فریمورکهایی الگوی MVVM به عبارت زیر است:
· MicroModels
· .NET Community Toolkit
· DevExpress MVVM
· DotVVM open source project
· MVVMLight Toolkit
· Jellyfish
· ReactiveUI
· Mugen MVVM Toolkit
· Uno Platform - Open Source
· Rascl
· MvvmCross
· FreshMvvm
· Angular
· Aurelia
· Durandal
· Ember.js
· Ext JS
· Knockout.js
· Omi.js
· Oracle JET
· Reactjs
· Svelte
· Vue.js
· Xamlcc
همانطور که مشاهده کردید MVVM یک پترن هست. یعنی الگویی برای اینکه برنامهای ساختار یافتهتر داشته باشیم. امیدوارم یاد گرفته باشید که چرا MVVMیکی از الگوهای طراحی محبوب است.
در پایان این «این مطلب، بخشی از تمرینهای درس معماری نرمافزار در دانشگاه شهیدبهشتی است»
https://www.raywenderlich.com/34-design-patterns-by-tutorials-mvvm
https://www.geeksforgeeks.org/introduction-to-model-view-view-model-mvvm/
https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel
https://www.wintellect.com/model-view-viewmodel-mvvm-explained/
https://www.tutorialspoint.com/mvvm/mvvm_introduction.htm