راهنمای معماری اپلیکیشن

این راهنما شامل بهترین شیوه ها و معماری توصیه شده برای ساخت برنامه های قوی و با کیفیت است.

تجربیات کاربر اپلیکیشن موبایل

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

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

با توجه به شرایط این محیط، این امکان وجود دارد که اجزای برنامه شما به صورت تکی و خارج از نظم راه اندازی شوند و سیستم عامل یا کاربر هر زمان که بخواهد می تواند آنها را از بین ببرد. از آنجایی که این رویدادها تحت کنترل شما نیستند، نباید هیچ داده یا وضعیت برنامه را در اجزای برنامه خود ذخیره یا در حافظه نگه دارید، و اجزای برنامه شما نباید به یکدیگر وابسته باشند.

اصول رایج معماری

اگر نباید از اجزای برنامه برای ذخیره داده ها و حالت برنامه استفاده کنید، چگونه باید برنامه خود را به جای آن طراحی کنید؟

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

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

تفکیک نگرانی ها

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

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

درایو UI از مدل های داده

اصل مهم دیگر این است که شما باید رابط کاربری خود را از مدل های داده، ترجیحاً مدل های پایدار، هدایت کنید. مدل های داده نشان دهنده داده های یک برنامه هستند. آنها مستقل از عناصر UI و سایر اجزای برنامه شما هستند. این بدان معنی است که آنها به چرخه عمر UI و مؤلفه برنامه مرتبط نیستند، اما زمانی که سیستم عامل تصمیم به حذف فرآیند برنامه از حافظه بگیرد، همچنان از بین می روند.

مدل های پایدار به دلایل زیر ایده آل هستند:

  • اگر سیستم عامل Android برنامه شما را برای آزاد کردن منابع از بین ببرد، کاربران شما اطلاعات را از دست نمی دهند.

  • برنامه شما در مواردی که اتصال شبکه ضعیف است یا در دسترس نیست به کار خود ادامه می دهد.

اگر معماری برنامه خود را بر اساس کلاس های مدل داده ای قرار دهید، برنامه خود را آزمایش پذیرتر و قوی تر می کنید.

منبع واحد حقیقت

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

این الگو مزایای متعددی را به همراه دارد:

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

در یک برنامه آفلاین اول، منبع حقیقت برای داده های برنامه معمولاً یک پایگاه داده است. در برخی موارد دیگر، منبع حقیقت می تواند یک ViewModel یا حتی UI باشد.

جریان داده های یک طرفه

اصل منفرد حقیقت اغلب در راهنماهای ما با الگوی جریان داده های یک طرفه (UDF) استفاده می شود. در UDF، حالت فقط در یک جهت جریان دارد. رویدادهایی که جریان داده ها را در جهت مخالف تغییر می دهند.

در اندروید، وضعیت یا داده‌ها معمولاً از انواع سلسله مراتبی با دامنه بالاتر به موارد با دامنه پایین‌تر منتقل می‌شوند. رویدادها معمولاً از انواع با دامنه پایین تر شروع می شوند تا زمانی که به SSOT برای نوع داده مربوطه برسند. به عنوان مثال، داده های برنامه معمولاً از منابع داده به UI جریان می یابد. رویدادهای کاربر مانند فشار دادن دکمه‌ها از UI به SSOT جریان می‌یابند که در آن داده‌های برنامه تغییر یافته و در یک نوع تغییرناپذیر نمایش داده می‌شوند.

این الگو سازگاری داده ها را بهتر تضمین می کند، کمتر مستعد خطا است، اشکال زدایی آسان تر است و تمام مزایای الگوی SSOT را به ارمغان می آورد.

این بخش نشان می‌دهد که چگونه برنامه خود را مطابق بهترین شیوه‌های توصیه شده ساختار دهید.

با توجه به اصول رایج معماری که در بخش قبل ذکر شد، هر برنامه باید حداقل دو لایه داشته باشد:

  • لایه رابط کاربری که داده های برنامه را روی صفحه نمایش می دهد.
  • لایه داده ای که حاوی منطق تجاری برنامه شما است و داده های برنامه را در معرض دید قرار می دهد.

شما می توانید یک لایه اضافی به نام لایه دامنه اضافه کنید تا تعاملات بین UI و لایه های داده را ساده کرده و مجددا استفاده کنید.

در یک معماری برنامه معمولی، لایه UI داده های برنامه را از لایه داده یا از لایه دامنه اختیاری، که بین لایه UI و لایه داده قرار دارد، دریافت می کند.
شکل 1. نمودار یک معماری برنامه معمولی.

معماری اپلیکیشن مدرن

این معماری برنامه مدرن استفاده از تکنیک های زیر را از جمله موارد دیگر تشویق می کند:

  • معماری واکنشی و لایه ای.
  • جریان داده های یک طرفه (UDF) در تمام لایه های برنامه.
  • یک لایه UI با دارندگان حالت برای مدیریت پیچیدگی UI.
  • کوروتین ها و جریان ها.
  • بهترین روش های تزریق وابستگی

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

لایه رابط کاربری

نقش لایه UI (یا لایه ارائه ) نمایش داده های برنامه روی صفحه است. هر زمان که داده ها تغییر کند، چه به دلیل تعامل کاربر (مانند فشار دادن یک دکمه) یا ورودی خارجی (مانند پاسخ شبکه)، رابط کاربری باید به روز شود تا تغییرات را منعکس کند.

لایه UI از دو چیز تشکیل شده است:

  • عناصر رابط کاربری که داده ها را روی صفحه نمایش می دهد. شما این عناصر را با استفاده از توابع Views یا Jetpack Compose می سازید.
  • دارندگان حالت (مانند کلاس‌های ViewModel ) که داده‌ها را نگه می‌دارند، آن‌ها را در معرض UI قرار می‌دهند و منطق را مدیریت می‌کنند.
در یک معماری معمولی، عناصر UI لایه UI به دارندگان حالت بستگی دارد، که به نوبه خود به کلاس هایی از لایه داده یا لایه دامنه اختیاری بستگی دارد.
شکل 2. نقش لایه رابط کاربری در معماری برنامه.

برای کسب اطلاعات بیشتر در مورد این لایه، به صفحه لایه UI مراجعه کنید.

لایه داده

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

لایه داده از مخازنی ساخته شده است که هر کدام می توانند حاوی صفر تا بسیاری از منابع داده باشند. شما باید برای هر نوع داده متفاوتی که در برنامه خود مدیریت می کنید، یک کلاس مخزن ایجاد کنید. به عنوان مثال، ممکن است یک کلاس MoviesRepository برای داده های مربوط به فیلم ها، یا یک کلاس PaymentsRepository برای داده های مربوط به پرداخت ها ایجاد کنید.

در یک معماری معمولی، مخازن لایه داده داده ها را در اختیار بقیه برنامه قرار می دهند و به منابع داده بستگی دارند.
شکل 3. نقش لایه داده در معماری برنامه.

کلاس های مخزن وظایف زیر را بر عهده دارند:

  • نمایش داده‌ها به بقیه برنامه.
  • متمرکز کردن تغییرات در داده ها
  • حل تضاد بین منابع داده چندگانه
  • انتزاع منابع داده از بقیه برنامه.
  • حاوی منطق تجاری

هر کلاس منبع داده باید مسئولیت کار با یک منبع داده را داشته باشد که می تواند یک فایل، یک منبع شبکه یا یک پایگاه داده محلی باشد. کلاس های منبع داده پل بین برنامه و سیستم برای عملیات داده ها هستند.

برای کسب اطلاعات بیشتر در مورد این لایه، به صفحه لایه داده مراجعه کنید.

لایه دامنه

لایه دامنه یک لایه اختیاری است که بین لایه های رابط کاربری و داده قرار می گیرد.

لایه دامنه مسئول کپسوله کردن منطق تجاری پیچیده یا منطق تجاری ساده است که توسط چندین ViewModels استفاده مجدد می شود. این لایه اختیاری است زیرا همه برنامه ها این الزامات را ندارند. شما باید فقط در صورت نیاز از آن استفاده کنید - برای مثال، برای رسیدگی به پیچیدگی یا استفاده مجدد.

هنگامی که شامل می شود، لایه دامنه اختیاری وابستگی هایی را به لایه UI ارائه می دهد و به لایه داده بستگی دارد.
شکل 4. نقش لایه دامنه در معماری برنامه.

کلاس‌های این لایه معمولاً موارد استفاده یا interactor نامیده می‌شوند. هر مورد استفاده باید مسئولیت یک عملکرد واحد را داشته باشد. به عنوان مثال، اگر چندین ViewModel برای نمایش پیام مناسب روی صفحه به مناطق زمانی متکی باشند، برنامه شما می‌تواند یک کلاس GetTimeZoneUseCase داشته باشد.

برای کسب اطلاعات بیشتر در مورد این لایه، به صفحه لایه دامنه مراجعه کنید.

مدیریت وابستگی بین اجزا

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

  • تزریق وابستگی (DI) : تزریق وابستگی به کلاس ها اجازه می دهد تا وابستگی های خود را بدون ساختن آنها تعریف کنند. در زمان اجرا، کلاس دیگری مسئول ارائه این وابستگی ها است.
  • Service locator : الگوی یاب سرویس یک رجیستری را ارائه می دهد که در آن کلاس ها می توانند وابستگی های خود را به جای ساختن آنها بدست آورند.

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

توصیه می کنیم الگوهای تزریق وابستگی را دنبال کنید و از کتابخانه Hilt در برنامه های اندروید استفاده کنید. Hilt به طور خودکار اشیاء را با قدم زدن در درخت وابستگی ایجاد می کند، تضمین های زمان کامپایل در وابستگی ها را ارائه می دهد و کانتینرهای وابستگی را برای کلاس های فریمورک اندروید ایجاد می کند.

بهترین شیوه های عمومی

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

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

داده ها را در اجزای برنامه ذخیره نکنید.

از تعیین نقاط ورودی برنامه خود - مانند فعالیت ها، خدمات و گیرنده های پخش - به عنوان منابع داده خودداری کنید. در عوض، آنها فقط باید با سایر مؤلفه ها برای بازیابی زیرمجموعه داده هایی که مربوط به آن نقطه ورودی هستند هماهنگ شوند. هر مؤلفه برنامه بسته به تعامل کاربر با دستگاه خود و سلامت کلی فعلی سیستم، نسبتاً کوتاه مدت است.

کاهش وابستگی به کلاس های اندروید.

اجزای برنامه شما باید تنها کلاس‌هایی باشند که به APIهای Android Framework SDK مانند Context یا Toast متکی هستند. انتزاع کلاس‌های دیگر در برنامه‌تان به دور از آن‌ها به آزمایش‌پذیری کمک می‌کند و جفت شدن در برنامه شما را کاهش می‌دهد.

مرزهای مسئولیت مشخصی بین ماژول های مختلف در برنامه خود ایجاد کنید.

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

از هر ماژول تا حد امکان کمتر نمایش داده شود.

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

روی هسته منحصر به فرد برنامه خود تمرکز کنید تا از سایر برنامه ها متمایز شود.

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

در نظر بگیرید که چگونه هر بخش از برنامه خود را به تنهایی قابل آزمایش کنید.

به عنوان مثال، داشتن یک API کاملاً تعریف شده برای واکشی داده ها از شبکه، آزمایش ماژولی را که آن داده ها را در یک پایگاه داده محلی حفظ می کند، آسان تر می کند. اگر در عوض، منطق این دو ماژول را در یک مکان ترکیب کنید، یا کد شبکه خود را در کل پایه کد خود توزیع کنید، آزمایش مؤثر بسیار دشوارتر - اگر غیرممکن نباشد - می شود.

تیپ ها مسئول خط مشی همزمانی خود هستند.

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

تا حد امکان داده های مرتبط و تازه را حفظ کنید.

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

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

پیاده سازی یک معماری خوب در برنامه شما مزایای زیادی برای پروژه و تیم های مهندسی به همراه دارد:

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

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

نمونه ها

نمونه‌های گوگل زیر معماری اپلیکیشن خوب را نشان می‌دهند. برای دیدن این راهنمایی در عمل، آنها را کاوش کنید:

{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% endverbatim %}،

این راهنما شامل بهترین شیوه ها و معماری توصیه شده برای ساخت برنامه های قوی و با کیفیت است.

تجربیات کاربر اپلیکیشن موبایل

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

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

با توجه به شرایط این محیط، این امکان وجود دارد که اجزای برنامه شما به صورت تکی و خارج از نظم راه اندازی شوند و سیستم عامل یا کاربر هر زمان که بخواهد می تواند آنها را از بین ببرد. از آنجایی که این رویدادها تحت کنترل شما نیستند، نباید هیچ داده یا وضعیت برنامه را در اجزای برنامه خود ذخیره یا در حافظه نگه دارید، و اجزای برنامه شما نباید به یکدیگر وابسته باشند.

اصول رایج معماری

اگر نباید از اجزای برنامه برای ذخیره داده ها و حالت برنامه استفاده کنید، چگونه باید برنامه خود را به جای آن طراحی کنید؟

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

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

تفکیک نگرانی ها

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

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

درایو UI از مدل های داده

اصل مهم دیگر این است که شما باید رابط کاربری خود را از مدل های داده، ترجیحاً مدل های پایدار، هدایت کنید. مدل های داده نشان دهنده داده های یک برنامه هستند. آنها مستقل از عناصر UI و سایر اجزای برنامه شما هستند. این بدان معنی است که آنها به چرخه عمر UI و مؤلفه برنامه مرتبط نیستند، اما زمانی که سیستم عامل تصمیم به حذف فرآیند برنامه از حافظه بگیرد، همچنان از بین می روند.

مدل های پایدار به دلایل زیر ایده آل هستند:

  • اگر سیستم عامل Android برنامه شما را برای آزاد کردن منابع از بین ببرد، کاربران شما اطلاعات را از دست نمی دهند.

  • برنامه شما در مواردی که اتصال شبکه ضعیف است یا در دسترس نیست به کار خود ادامه می دهد.

اگر معماری برنامه خود را بر اساس کلاس های مدل داده ای قرار دهید، برنامه خود را آزمایش پذیرتر و قوی تر می کنید.

منبع واحد حقیقت

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

این الگو مزایای متعددی را به همراه دارد:

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

در یک برنامه آفلاین اول، منبع حقیقت برای داده های برنامه معمولاً یک پایگاه داده است. در برخی موارد دیگر، منبع حقیقت می تواند یک ViewModel یا حتی UI باشد.

جریان داده های یک طرفه

اصل منفرد حقیقت اغلب در راهنماهای ما با الگوی جریان داده های یک طرفه (UDF) استفاده می شود. در UDF، حالت فقط در یک جهت جریان دارد. رویدادهایی که جریان داده ها را در جهت مخالف تغییر می دهند.

در اندروید، وضعیت یا داده‌ها معمولاً از انواع سلسله مراتبی با دامنه بالاتر به موارد با دامنه پایین‌تر منتقل می‌شوند. رویدادها معمولاً از انواع با دامنه پایین تر شروع می شوند تا زمانی که به SSOT برای نوع داده مربوطه برسند. به عنوان مثال، داده های برنامه معمولاً از منابع داده به UI جریان می یابد. رویدادهای کاربر مانند فشار دادن دکمه‌ها از UI به SSOT جریان می‌یابند که در آن داده‌های برنامه تغییر یافته و در یک نوع تغییرناپذیر نمایش داده می‌شوند.

این الگو سازگاری داده ها را بهتر تضمین می کند، کمتر مستعد خطا است، اشکال زدایی آسان تر است و تمام مزایای الگوی SSOT را به ارمغان می آورد.

این بخش نشان می‌دهد که چگونه برنامه خود را مطابق بهترین شیوه‌های توصیه شده ساختار دهید.

با توجه به اصول رایج معماری که در بخش قبل ذکر شد، هر برنامه باید حداقل دو لایه داشته باشد:

  • لایه رابط کاربری که داده های برنامه را روی صفحه نمایش می دهد.
  • لایه داده ای که حاوی منطق تجاری برنامه شما است و داده های برنامه را در معرض دید قرار می دهد.

شما می توانید یک لایه اضافی به نام لایه دامنه اضافه کنید تا تعاملات بین UI و لایه های داده را ساده کرده و مجددا استفاده کنید.

در یک معماری برنامه معمولی، لایه UI داده های برنامه را از لایه داده یا از لایه دامنه اختیاری، که بین لایه UI و لایه داده قرار دارد، دریافت می کند.
شکل 1. نمودار یک معماری برنامه معمولی.

معماری اپلیکیشن مدرن

این معماری برنامه مدرن استفاده از تکنیک های زیر را از جمله موارد دیگر تشویق می کند:

  • معماری واکنشی و لایه ای.
  • جریان داده های یک طرفه (UDF) در تمام لایه های برنامه.
  • یک لایه UI با دارندگان حالت برای مدیریت پیچیدگی UI.
  • کوروتین ها و جریان ها.
  • بهترین روش های تزریق وابستگی

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

لایه رابط کاربری

نقش لایه UI (یا لایه ارائه ) نمایش داده های برنامه روی صفحه است. هر زمان که داده ها تغییر کند، چه به دلیل تعامل کاربر (مانند فشار دادن یک دکمه) یا ورودی خارجی (مانند پاسخ شبکه)، رابط کاربری باید به روز شود تا تغییرات را منعکس کند.

لایه UI از دو چیز تشکیل شده است:

  • عناصر رابط کاربری که داده ها را روی صفحه نمایش می دهد. شما این عناصر را با استفاده از توابع Views یا Jetpack Compose می سازید.
  • دارندگان حالت (مانند کلاس‌های ViewModel ) که داده‌ها را نگه می‌دارند، آن‌ها را در معرض UI قرار می‌دهند و منطق را مدیریت می‌کنند.
در یک معماری معمولی، عناصر UI لایه UI به دارندگان حالت بستگی دارد، که به نوبه خود به کلاس هایی از لایه داده یا لایه دامنه اختیاری بستگی دارد.
شکل 2. نقش لایه رابط کاربری در معماری برنامه.

برای کسب اطلاعات بیشتر در مورد این لایه، به صفحه لایه UI مراجعه کنید.

لایه داده

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

لایه داده از مخازنی ساخته شده است که هر کدام می توانند حاوی صفر تا بسیاری از منابع داده باشند. شما باید برای هر نوع داده متفاوتی که در برنامه خود مدیریت می کنید، یک کلاس مخزن ایجاد کنید. به عنوان مثال، ممکن است یک کلاس MoviesRepository برای داده های مربوط به فیلم ها، یا یک کلاس PaymentsRepository برای داده های مربوط به پرداخت ها ایجاد کنید.

در یک معماری معمولی، مخازن لایه داده داده ها را در اختیار بقیه برنامه قرار می دهند و به منابع داده بستگی دارند.
شکل 3. نقش لایه داده در معماری برنامه.

کلاس های مخزن وظایف زیر را بر عهده دارند:

  • نمایش داده‌ها به بقیه برنامه.
  • متمرکز کردن تغییرات در داده ها
  • حل تضاد بین منابع داده چندگانه
  • انتزاع منابع داده از بقیه برنامه.
  • حاوی منطق تجاری

هر کلاس منبع داده باید مسئولیت کار با یک منبع داده را داشته باشد که می تواند یک فایل، یک منبع شبکه یا یک پایگاه داده محلی باشد. کلاس های منبع داده پل بین برنامه و سیستم برای عملیات داده ها هستند.

برای کسب اطلاعات بیشتر در مورد این لایه، به صفحه لایه داده مراجعه کنید.

لایه دامنه

لایه دامنه یک لایه اختیاری است که بین لایه های رابط کاربری و داده قرار می گیرد.

لایه دامنه مسئول کپسوله کردن منطق تجاری پیچیده یا منطق تجاری ساده است که توسط چندین ViewModels استفاده مجدد می شود. این لایه اختیاری است زیرا همه برنامه ها این الزامات را ندارند. شما باید فقط در صورت نیاز از آن استفاده کنید - برای مثال، برای رسیدگی به پیچیدگی یا استفاده مجدد.

هنگامی که شامل می شود، لایه دامنه اختیاری وابستگی هایی را به لایه UI ارائه می دهد و به لایه داده بستگی دارد.
شکل 4. نقش لایه دامنه در معماری برنامه.

کلاس‌های این لایه معمولاً موارد استفاده یا interactor نامیده می‌شوند. هر مورد استفاده باید مسئولیت یک عملکرد واحد را داشته باشد. به عنوان مثال، اگر چندین ViewModel برای نمایش پیام مناسب روی صفحه به مناطق زمانی متکی باشند، برنامه شما می‌تواند یک کلاس GetTimeZoneUseCase داشته باشد.

برای کسب اطلاعات بیشتر در مورد این لایه، به صفحه لایه دامنه مراجعه کنید.

مدیریت وابستگی بین اجزا

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

  • تزریق وابستگی (DI) : تزریق وابستگی به کلاس ها اجازه می دهد تا وابستگی های خود را بدون ساختن آنها تعریف کنند. در زمان اجرا، کلاس دیگری مسئول ارائه این وابستگی ها است.
  • Service locator : الگوی یاب سرویس یک رجیستری را ارائه می دهد که در آن کلاس ها می توانند وابستگی های خود را به جای ساختن آنها بدست آورند.

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

توصیه می کنیم الگوهای تزریق وابستگی را دنبال کنید و از کتابخانه Hilt در برنامه های اندروید استفاده کنید. Hilt به طور خودکار اشیاء را با قدم زدن در درخت وابستگی ایجاد می کند، تضمین های زمان کامپایل در وابستگی ها را ارائه می دهد و کانتینرهای وابستگی را برای کلاس های فریمورک اندروید ایجاد می کند.

بهترین شیوه های عمومی

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

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

داده ها را در اجزای برنامه ذخیره نکنید.

از تعیین نقاط ورودی برنامه خود - مانند فعالیت ها، خدمات و گیرنده های پخش - به عنوان منابع داده خودداری کنید. در عوض، آنها فقط باید با سایر مؤلفه ها برای بازیابی زیرمجموعه داده هایی که مربوط به آن نقطه ورودی هستند هماهنگ شوند. هر مؤلفه برنامه بسته به تعامل کاربر با دستگاه خود و سلامت کلی فعلی سیستم، نسبتاً کوتاه مدت است.

کاهش وابستگی به کلاس های اندروید.

اجزای برنامه شما باید تنها کلاس‌هایی باشند که به APIهای Android Framework SDK مانند Context یا Toast متکی هستند. انتزاع کلاس‌های دیگر در برنامه‌تان به دور از آن‌ها به آزمایش‌پذیری کمک می‌کند و جفت شدن در برنامه شما را کاهش می‌دهد.

مرزهای مسئولیت مشخصی بین ماژول های مختلف در برنامه خود ایجاد کنید.

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

از هر ماژول تا حد امکان کمتر نمایش داده شود.

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

روی هسته منحصر به فرد برنامه خود تمرکز کنید تا از سایر برنامه ها متمایز شود.

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

در نظر بگیرید که چگونه هر بخش از برنامه خود را به تنهایی قابل آزمایش کنید.

به عنوان مثال، داشتن یک API کاملاً تعریف شده برای واکشی داده ها از شبکه، آزمایش ماژولی را که آن داده ها را در یک پایگاه داده محلی حفظ می کند، آسان تر می کند. اگر در عوض، منطق این دو ماژول را در یک مکان ترکیب کنید، یا کد شبکه خود را در کل پایه کد خود توزیع کنید، آزمایش مؤثر بسیار دشوارتر - اگر غیرممکن نباشد - می شود.

تیپ ها مسئول خط مشی همزمانی خود هستند.

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

تا حد امکان داده های مرتبط و تازه را حفظ کنید.

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

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

پیاده سازی یک معماری خوب در برنامه شما مزایای زیادی برای پروژه و تیم های مهندسی به همراه دارد:

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

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

نمونه ها

نمونه‌های گوگل زیر معماری اپلیکیشن خوب را نشان می‌دهند. برای دیدن این راهنمایی در عمل، آنها را کاوش کنید:

{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% endverbatim %}،

این راهنما شامل بهترین شیوه ها و معماری توصیه شده برای ساخت برنامه های قوی و با کیفیت است.

تجربیات کاربر اپلیکیشن موبایل

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

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

با توجه به شرایط این محیط، این امکان وجود دارد که اجزای برنامه شما به صورت تکی و خارج از نظم راه اندازی شوند و سیستم عامل یا کاربر هر زمان که بخواهد می تواند آنها را از بین ببرد. از آنجایی که این رویدادها تحت کنترل شما نیستند، نباید هیچ داده یا وضعیت برنامه را در اجزای برنامه خود ذخیره یا در حافظه نگه دارید، و اجزای برنامه شما نباید به یکدیگر وابسته باشند.

اصول رایج معماری

اگر نباید از اجزای برنامه برای ذخیره داده ها و حالت برنامه استفاده کنید، چگونه باید برنامه خود را به جای آن طراحی کنید؟

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

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

تفکیک نگرانی ها

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

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

درایو UI از مدل های داده

اصل مهم دیگر این است که شما باید رابط کاربری خود را از مدل های داده، ترجیحاً مدل های پایدار، هدایت کنید. مدل های داده نشان دهنده داده های یک برنامه هستند. آنها مستقل از عناصر UI و سایر اجزای برنامه شما هستند. این بدان معنی است که آنها به چرخه عمر UI و مؤلفه برنامه مرتبط نیستند، اما زمانی که سیستم عامل تصمیم به حذف فرآیند برنامه از حافظه بگیرد، همچنان از بین می روند.

مدل های پایدار به دلایل زیر ایده آل هستند:

  • اگر سیستم عامل Android برنامه شما را برای آزاد کردن منابع از بین ببرد، کاربران شما اطلاعات را از دست نمی دهند.

  • برنامه شما در مواردی که اتصال شبکه ضعیف است یا در دسترس نیست به کار خود ادامه می دهد.

اگر معماری برنامه خود را بر اساس کلاس های مدل داده ای قرار دهید، برنامه خود را آزمایش پذیرتر و قوی تر می کنید.

منبع واحد حقیقت

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

این الگو مزایای متعددی را به همراه دارد:

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

در یک برنامه آفلاین اول، منبع حقیقت برای داده های برنامه معمولاً یک پایگاه داده است. در برخی موارد دیگر، منبع حقیقت می تواند یک ViewModel یا حتی UI باشد.

جریان داده های یک طرفه

اصل منفرد حقیقت اغلب در راهنماهای ما با الگوی جریان داده های یک طرفه (UDF) استفاده می شود. در UDF، حالت فقط در یک جهت جریان دارد. رویدادهایی که جریان داده ها را در جهت مخالف تغییر می دهند.

در اندروید، وضعیت یا داده‌ها معمولاً از انواع سلسله مراتبی با دامنه بالاتر به موارد با دامنه پایین‌تر منتقل می‌شوند. رویدادها معمولاً از انواع با دامنه پایین تر شروع می شوند تا زمانی که به SSOT برای نوع داده مربوطه برسند. به عنوان مثال، داده های برنامه معمولاً از منابع داده به UI جریان می یابد. رویدادهای کاربر مانند فشار دادن دکمه‌ها از UI به SSOT جریان می‌یابند که در آن داده‌های برنامه تغییر یافته و در یک نوع تغییرناپذیر نمایش داده می‌شوند.

این الگو سازگاری داده ها را بهتر تضمین می کند، کمتر مستعد خطا است، اشکال زدایی آسان تر است و تمام مزایای الگوی SSOT را به ارمغان می آورد.

این بخش نشان می‌دهد که چگونه برنامه خود را مطابق بهترین شیوه‌های توصیه شده ساختار دهید.

با توجه به اصول رایج معماری که در بخش قبل ذکر شد، هر برنامه باید حداقل دو لایه داشته باشد:

  • لایه رابط کاربری که داده های برنامه را روی صفحه نمایش می دهد.
  • لایه داده ای که حاوی منطق تجاری برنامه شما است و داده های برنامه را در معرض دید قرار می دهد.

شما می توانید یک لایه اضافی به نام لایه دامنه اضافه کنید تا تعاملات بین UI و لایه های داده را ساده کرده و مجددا استفاده کنید.

در یک معماری برنامه معمولی، لایه UI داده های برنامه را از لایه داده یا از لایه دامنه اختیاری، که بین لایه UI و لایه داده قرار دارد، دریافت می کند.
شکل 1. نمودار یک معماری برنامه معمولی.

معماری اپلیکیشن مدرن

این معماری برنامه مدرن استفاده از تکنیک های زیر را از جمله موارد دیگر تشویق می کند:

  • معماری واکنشی و لایه ای.
  • جریان داده های یک طرفه (UDF) در تمام لایه های برنامه.
  • یک لایه UI با دارندگان حالت برای مدیریت پیچیدگی UI.
  • کوروتین ها و جریان ها.
  • بهترین روش های تزریق وابستگی

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

لایه رابط کاربری

نقش لایه UI (یا لایه ارائه ) نمایش داده های برنامه روی صفحه است. هر زمان که داده ها تغییر کند، چه به دلیل تعامل کاربر (مانند فشار دادن یک دکمه) یا ورودی خارجی (مانند پاسخ شبکه)، رابط کاربری باید به روز شود تا تغییرات را منعکس کند.

لایه UI از دو چیز تشکیل شده است:

  • عناصر رابط کاربری که داده ها را روی صفحه نمایش می دهد. شما این عناصر را با استفاده از توابع Views یا Jetpack Compose می سازید.
  • دارندگان حالت (مانند کلاس‌های ViewModel ) که داده‌ها را نگه می‌دارند، آن‌ها را در معرض UI قرار می‌دهند و منطق را مدیریت می‌کنند.
در یک معماری معمولی، عناصر UI لایه UI به دارندگان حالت بستگی دارد، که به نوبه خود به کلاس هایی از لایه داده یا لایه دامنه اختیاری بستگی دارد.
شکل 2. نقش لایه رابط کاربری در معماری برنامه.

برای کسب اطلاعات بیشتر در مورد این لایه، به صفحه لایه UI مراجعه کنید.

لایه داده

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

لایه داده از مخازنی ساخته شده است که هر کدام می توانند حاوی صفر تا بسیاری از منابع داده باشند. شما باید برای هر نوع داده متفاوتی که در برنامه خود مدیریت می کنید، یک کلاس مخزن ایجاد کنید. به عنوان مثال، ممکن است یک کلاس MoviesRepository برای داده های مربوط به فیلم ها، یا یک کلاس PaymentsRepository برای داده های مربوط به پرداخت ها ایجاد کنید.

در یک معماری معمولی، مخازن لایه داده داده ها را در اختیار بقیه برنامه قرار می دهند و به منابع داده بستگی دارند.
شکل 3. نقش لایه داده در معماری برنامه.

کلاس های مخزن وظایف زیر را بر عهده دارند:

  • نمایش داده‌ها به بقیه برنامه.
  • متمرکز کردن تغییرات در داده ها
  • حل تضاد بین منابع داده چندگانه
  • انتزاع منابع داده از بقیه برنامه.
  • حاوی منطق تجاری

هر کلاس منبع داده باید مسئولیت کار با یک منبع داده را داشته باشد که می تواند یک فایل، یک منبع شبکه یا یک پایگاه داده محلی باشد. کلاس های منبع داده پل بین برنامه و سیستم برای عملیات داده ها هستند.

برای کسب اطلاعات بیشتر در مورد این لایه، به صفحه لایه داده مراجعه کنید.

لایه دامنه

لایه دامنه یک لایه اختیاری است که بین لایه های رابط کاربری و داده قرار می گیرد.

لایه دامنه مسئول کپسوله کردن منطق تجاری پیچیده یا منطق تجاری ساده است که توسط چندین ViewModels استفاده مجدد می شود. این لایه اختیاری است زیرا همه برنامه ها این الزامات را ندارند. شما باید فقط در صورت نیاز از آن استفاده کنید - برای مثال، برای رسیدگی به پیچیدگی یا استفاده مجدد.

هنگامی که شامل می شود، لایه دامنه اختیاری وابستگی هایی را به لایه UI ارائه می دهد و به لایه داده بستگی دارد.
شکل 4. نقش لایه دامنه در معماری برنامه.

کلاس‌های این لایه معمولاً موارد استفاده یا interactor نامیده می‌شوند. هر مورد استفاده باید مسئولیت یک عملکرد واحد را داشته باشد. به عنوان مثال، اگر چندین ViewModel برای نمایش پیام مناسب روی صفحه به مناطق زمانی متکی باشند، برنامه شما می‌تواند یک کلاس GetTimeZoneUseCase داشته باشد.

برای کسب اطلاعات بیشتر در مورد این لایه، به صفحه لایه دامنه مراجعه کنید.

مدیریت وابستگی بین اجزا

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

  • تزریق وابستگی (DI) : تزریق وابستگی به کلاس ها اجازه می دهد تا وابستگی های خود را بدون ساختن آنها تعریف کنند. در زمان اجرا، کلاس دیگری مسئول ارائه این وابستگی ها است.
  • Service locator : الگوی یاب سرویس یک رجیستری را ارائه می دهد که در آن کلاس ها می توانند وابستگی های خود را به جای ساختن آنها بدست آورند.

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

توصیه می کنیم الگوهای تزریق وابستگی را دنبال کنید و از کتابخانه Hilt در برنامه های اندروید استفاده کنید. Hilt به طور خودکار اشیاء را با قدم زدن در درخت وابستگی ایجاد می کند، تضمین های زمان کامپایل در وابستگی ها را ارائه می دهد و کانتینرهای وابستگی را برای کلاس های فریمورک اندروید ایجاد می کند.

بهترین شیوه های عمومی

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

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

داده ها را در اجزای برنامه ذخیره نکنید.

از تعیین نقاط ورودی برنامه خود - مانند فعالیت ها، خدمات و گیرنده های پخش - به عنوان منابع داده خودداری کنید. در عوض، آنها فقط باید با سایر مؤلفه ها برای بازیابی زیرمجموعه داده هایی که مربوط به آن نقطه ورودی هستند هماهنگ شوند. هر مؤلفه برنامه بسته به تعامل کاربر با دستگاه خود و سلامت کلی فعلی سیستم، نسبتاً کوتاه مدت است.

کاهش وابستگی به کلاس های اندروید.

اجزای برنامه شما باید تنها کلاس‌هایی باشند که به APIهای Android Framework SDK مانند Context یا Toast متکی هستند. انتزاع کلاس‌های دیگر در برنامه‌تان به دور از آن‌ها به آزمایش‌پذیری کمک می‌کند و جفت شدن در برنامه شما را کاهش می‌دهد.

مرزهای مسئولیت مشخصی بین ماژول های مختلف در برنامه خود ایجاد کنید.

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

از هر ماژول تا حد امکان کمتر نمایش داده شود.

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

روی هسته منحصر به فرد برنامه خود تمرکز کنید تا از سایر برنامه ها متمایز شود.

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

در نظر بگیرید که چگونه هر بخش از برنامه خود را به تنهایی قابل آزمایش کنید.

به عنوان مثال، داشتن یک API کاملاً تعریف شده برای واکشی داده ها از شبکه، آزمایش ماژولی را که آن داده ها را در یک پایگاه داده محلی حفظ می کند، آسان تر می کند. اگر در عوض، منطق این دو ماژول را در یک مکان ترکیب کنید، یا کد شبکه خود را در کل پایه کد خود توزیع کنید، آزمایش مؤثر بسیار دشوارتر - اگر غیرممکن نباشد - می شود.

تیپ ها مسئول خط مشی همزمانی خود هستند.

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

تا حد امکان داده های مرتبط و تازه را حفظ کنید.

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

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

پیاده سازی یک معماری خوب در برنامه شما مزایای زیادی برای پروژه و تیم های مهندسی به همراه دارد:

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

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

نمونه ها

نمونه‌های گوگل زیر معماری اپلیکیشن خوب را نشان می‌دهند. برای دیدن این راهنمایی در عمل، آنها را کاوش کنید:

{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% آخر کلمه %}