تهیه پیش‌فروش دوره «آموزش تکمیلی و پروژه محور Spring Boot» با ۳۵% تخفیف - فقط تا ۱۳ شهریور 

چگونه در اندروید یک معماری تمیز پیاده‌سازی کنیم؟

چهارشنبه ۰۹ مرداد ۹۸ توسط سالار ساری نوایی

در سال 2016 یادگیری زبان جاوا را شروع کردم و در ابتدای سال 2017 به سمت اندروید کشیده شدم. از همان ابتدای کار می‌دانستم که معماری نرم افزار مد نظرم وجود دارد اما نمی‌دانستم که به چه شکل باید آن را روی کدم اعمال کنم. در ادامه آموزش‌های متعددی را مشاهده کردم اما هیچ کدام از آن‌ها برای یک تازه‌کار که تجربه‌ی چندانی در این زمینه نداشت مناسب نبودند و نتوانستند پاسخگوی نیازهای من باشند.

به همین دلیل تصمیم گرفته‌ام که در این مقاله، مطالبی که آموخته‌ام را به نحوی ارائه بدهم که اگر خودم در آن زمان آن را مطالعه می‌کردم، می‌توانستم اطلاعات زیادی درباره‌ی یک معماری تمیز در اندروید کسب کنم.

اهمیت معماری نرم افزار

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

یک معماری نرم افزار خوب باعث می‌شود که فهمیدن سیستم آسان‌تر شود و توسعه، نگه‌داری و پیاده‌سازی آن به راحتی صورت بگیرد. (کتاب Clean Architecture، فصل 15)

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

مثال:

  1. داده‌ها را از یک API دریافت کرده و نتایج را به کاربر نشان می‌دهیم.
  2. نتایج لیستی از نوشیدنی‌ها با نام، توضیحات، تصویر و محتویات نوشیدنی را نشان می‌دهد.
  3. نوشیدنی‌ها باید از کمترین امتیاز به بیشترین امتیاز مرتب شوند.

برای حل این مسئله نیاز است که کارهای زیر را انجام بدهیم:

  1. در ابتدا نیاز است که داده‌ها را از API دریافت کنیم.
  2. زمانی که داده‌ها را در اختیار داریم باید آن‌ها را به ترتیب امتیاز مرتب کنیم.
  3. اگر امتیاز نوشیدنی کمتر از 5 است در یک دایره‌ی سبز نشان داده شود. اگر امتیاز آن بین 5 و 8 است در یک دایره‌ی نارنجی و اگر بیشتر از 8 است در یک دایره‌ی قرمز قرار بگیرد.
  4. در انتها باید آن‌ها را در یک لیست نمایش بدهیم.

نامطلوب‌ترین راهکار چیست؟

راهکاری که توصیه نمی‌شود این است که یک کلاس ایجاد کنیم و تمامی 4 نکته‌ی ذکر شده را در آن کلاس پیاده‌سازی نماییم. هر کسی که از معماری نرم‌افزار آگاهی نداشته باشد به سراغ این راهکار خواهد رفت.

نتیجه‌ای که به دست می‌آید تاثیری در استفاده‌ی کاربر ندارد و ممکن است به چشم نیاید اما اگر نیاز باشد که سیستم را گسترش دهیم، متوجه می‌شویم که ساختار سیستم به گونه‌ای است که فهم، توسعه و پیاده‌سازی آن کار بسیار دشواری است.

چگونه می‌توانم معماری نرم‌افزار در اندروید را درک کنم؟

با یک مثال بسیار ساده توضیح می‌دهم. یک کارخانه‌ی ماشین‌سازی را تصور کنید که از پنج بخش تشکیل شده است.

  1. بخش اول که شاسی‌ها را تولید می‌کند.
  2. بخش دوم قطعات مکانیکی را به هم متصل می‌کند.
  3. بخش سوم مدار الکترونیکی را به وجود می‌آورد.
  4. بخش چهارم وظیفه‌ی رنگ‌آمیزی را بر عهده دارد.
  5. و بخش پنجم به جزئیات نهایی توجه می‌کند.

این یعنی که هر بخش وظایف به خصوص خود را دارد و تمامی بخش‌ها به صورت یک زنجیره با یکدیگر در ارتباط هستند تا به یک نتیجه برسند.

یکی از مزایای این کار این است که اگر پس از تولید ماشین مشکلی پیش بیاید یا خطایی صورت بگیرد، با کمی بررسی می‌توان فهمید که کدام بخش مسئول است و باید آن را برطرف کند و برای بخش‌های دیگر مزاحمتی ایجاد نمی‌شود.

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

همچنین اگر با گذر زمان بخواهیم کارخانه‌ی دیگری احداث کنیم و یک ماشین مدل جدید بسازیم، به اشتراک گذاشتن بخش‌های کاری بسیار آسان‌تر خواهد بود.

به‌کارگیری معماری نرم‌افزار در اندروید

هدف ما این است که تنها از یک کلاس برای انجام تمام وظایف توسعه‌ استفاده نکنیم. یعنی داده‌ها را از API دریافت کنیم، مرتب کنیم و آن ها را نمایش بدهیم اما به نحوی که در بخش‌های مختلف به نام لایه‌ها توزیع شده باشند (که به عنوان پکیج نمایش داده می‌شوند).

اما این لایه‌ها چه چیزهایی هستند؟

در ادامه قصد داریم که برای این مثال یک معماری تمیز به وجود بیاوریم که متشکل از سه لایه است و به پنج بخش مجزا تقسیم می‌شود:

لایه‌ی نمایش، لایه‌ی منطق تجاری و لایه‌ی داده‌ها.

1- لایه‌ی نمایش

لایه‌ی نمایش همان لایه‌ی کاربر است. همان رابط گرافیکی که اعمال کاربر را ثبت می‌کند و نتایج آن را نمایش می‌دهد. البته این لایه کارهای دیگری نیز انجام می‌دهد. لایه‌ی نمایش می‌تواند بررسی کند که هیچ گونه خطایی در فرمت ورودی‌ کاربر یا فرمت داده‌هایی که به نمایش گذاشته می‌شوند وجود نداشته باشد.

در مثال ما این وظایف میان لایه‌ی UI و لایه‌ی ViewModel تقسیم می‌شوند:

لایه‌ی UI شامل اکتیویتی‌ها و فرگمنت‌هایی است که اعمال کاربر را ثبت می‌کنند و داده‌ها را به نمایش می‌گذارند.

لایه‌ی ViewModel داده‌ها را فرمت می‌کند تا UI آن‌ها را به شکل خاصی نمایش بدهد. همچنین تضمین می‌کند که ورودی‌های کاربر در فرمت درستی وارد می‌شوند.

به جای استفاده از ViewModel می‌توانیم از لایه‌ی دیگری استفاده کنیم که همین کارها را انجام می‌دهد، تنها چیزی که اهمیت دارد این است که وظایف مختلف در لایه‌های مختلف قرار بگیرند.

در این مثال لایه‌ی UI نتایج نوشیدنی‌ها را نشان می‌دهد و ViewModel به شما می‌گوید که باید از کدام رنگ استفاده کنید. به عبارتی دیگر ViewModel امتیاز نوشیدنی را بررسی می‌کند تا UI بداند که چه رنگی به آن بدهد.

2- لایه‌ی منطق تجاری

در این لایه تمامی قوانینی که یک کسب و کار باید از آن‌ها پیروی کند، مد نظر قرار گرفته می‌شوند. برای این کار آن‌ها داده‌هایی که توسط کاربر در اختیارشان قرار داده می‌شود را دریافت می‌کنند و اعمال لازم را روی آن داده‌ها انجام می‌دهند. در مثال ما مرتب‌سازی نوشیدنی‌ها از کمترین تا بیشترین امتیاز نشان دهنده‌ی قوانینی است که در کلاس UseCase استفاده می‌شوند.

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

3- لایه‌ی داده

همان طور که از نامش پیداست، داده‌های ما در این لایه قرار دارند و از اینجا می‌توان به آن‌ها دسترسی داشت.

این اعمال میان لایه‌های Repository و Datasource تقسیم می‌شوند:

لایه‌ی Repository با منطق دسترسی به داده‌ها سروکار دارد. وظیفه‌ی شما این است که داده‌ها را به دست بیاورید و بررسی کنید که کجا هستند و این که در هر لحظه باید به کدام یک توجه کرد. به عنوان مثال، می‌توانید در ابتدا پایگاه داده را بررسی کنید و اگر در آنجا وجود نداشت، در وب به جستجوی آن بپردازید و آن را در یک پایگاه داده محلی ذخیره کنید و سپس داده‌ی ذخیره شده را ارسال کنید. به عبارتی دیگر، لایه‌ی Repository جریان دسترسی به داده‌ها را تعیین می‌کند. در مثال ما این لایه به طور مستقیم نوشیدنی‌ها را از لایه‌ی داده‌ای که با API در ارتباط است درخواست می‌کند.

لایه‌ی Datasource نیز متشکل از وظایفی است که باید انجام شوند تا بتوان به داده دسترسی پیدا کرد. در مثال ما، این لایه وظیفه‌ی پیاده‌سازی منطق دسترسی به داده‌های API از نوشیدنی‌ها را در عهده دارد.

این لایه‌ها چگونه با یکدیگر در ارتباط هستند؟

بگذارید ابتدا نگاهی به رویکرد تئوریک این قضیه بپردازیم و سپس به سراغ رویکرد عملی برویم.

در تئوری:

هر لایه باید تنها با دوستان نزدیک خود در ارتباط باشد! در این شرایط، اگر به شمای معماری نرم‌افزار نگاه کنیم می‌بینیم که:

  • UI می‌تواند فقط با ViewModel در ارتباط باشد.
  • ViewModel می‌تواند فقط با UseCase در ارتباط باشد.
  • UseCase می‌تواند فقط با Repository در ارتباط باشد.
  • و در انتها Repository می‌تواند فقط با Datasource در ارتباط باشد.

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

در عمل:

ما یک پکیج داریم که کلاس‌ها در آن ساخته می‌شوند و هر کلاس وظایف به خصوصی دارد.

در لایه‌ی رابط کاربری یا پکیج “ui”، اکتیویتی یا فرگمنت تولید می‌شود. این کلاس باید با لایه‌ی ViewModel در ارتباط باشد. برای این کار نیاز است کهاکتیویتی یک نمونه از شیء ViewModel به وجود بیاورد و LiveData های اعلام شده را بررسی کند.

در لایه‌ی منطق نمایش یا پکیچ “vm”، لایه‌ی ViewModel را به وجود می‌آوریم. این کلاس LiveData را تولید می‌کند که توسط اکتیویتی یا فرگمنت مورد بررسی قرار می‌گیرد. ViewModel می‌تواند با لایه‌ی UseCase ارتباط برقرار کند که برای این کار باید شیء UseCase نمونه‌سازی شود.

در لایه‌ی منطق تجاری یا پکیج “domain – use case”، کلاس UseCase را به وجود می‌آوریم. این کلاس با لایه‌ی بعدی خود که Repository است نمونه‌سازی می‌شود اما این کار را مستقیما با شیء انجام نمی‌دهد، بلکه آن را با یک رابط که در پکیج UseCase قرار دارد انجام می‌دهد. این کار به این دلیل صورت می‌گیرد که UseCase پایدارترین لایه است و این یعنی که کتابخانه‌ها و کلاس‌هایی که اهمیت دارند نیز باید این گونه باشند. ضمن این که Repository یکی از ناپایدارترین لایه‌های معماری است. در این شرایط، این لایه‌ی Repository است که از پکیج “domain – use case” ورودی می‌گیرد و UseCase از آن باخبر نمی‌شود. همان گونه که در تصویر زیر قابل مشاهده است، UseCase در مرکز معماری نرم‌افزار قرار دارد.

در مثال ما موجودیت (Entity) عبارت است از مدل داده‌ی لایه‌ی منطق تجاری.

در لایه‌ی Repository یا پکیج repository، کلاس RepositoryImp1 را به وجود می‌آوریم که این کلاس وظیفه‌ی پیاده‌سازی رابطی که در پکیج “domain – use case” قرار گرفته را بر عهده دارد. Repository لایه‌ی Datasource را فراخوانی می‌کند، به همین دلیل نیاز است که این کلاس نمونه‌سازی شود.

و در انتها در لایه‌ی Datasource یا پکیج “datasource”، کلاس Datasource را به وجود می‌آوریم که منطق جمع‌آوری داده از API را توسعه می‌دهد تا بتوان آن‌ها به یک مدل داده واگذار کرد که اعمال لازم روی داده‌ها صورت بگیرد. در مثال ما Datasource با کتابخانه‌ای نمونه‌سازی می‌شود که قرار است داده‌ها را مصرف کند، پس Datasource باید این کتابخانه را نمونه‌سازی کند تا بتواند تمامی متدهای آن را فراخوانی کند.

نکات پایانی درباره‌ی معماری نرم‌افزار

مشاهده کردیم که هر لایه از معماری نرم‌افزار وظایف خود را بر عهده دارد و میان این لایه‌ها ارتباط صورت می‌گیرد.

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

این مقاله‌ یک معرفی مختصر از معماری است و واجب است که اصول اساسی معماری به طور دقیق‌تر بررسی و پیاده‌سازی شوند. پیشنهاد ما این است که درباره‌ی تزریق وابستگی (Dependency Injection) مطالعه داشته باشید تا از نمونه‌سازی مستقیم اشیا در کلاس‌های معماری خودداری کنید و بتوانید با کمک Mockito و Junit آزمایش واحد انجام دهید.


کلیدواژه: اندروید معماری نرم‌افزار معماری اندروید معماری تمیز

منابع: proandroiddev.com

ارسال دیدگاه:
برای ارسال دیگاه باید به سیستم وارد شوید و یا ثبت نام کنید. ثبت نام چند لحظه بیشتر زمان شما را نمیگیرد.