Facade — یک الگوی طراحی ساختاری است که یک رابط سادهشده به زیرسیستم پیچیدهای از کلاسها ارائه میدهد. در توسعه موبایل، Facade اغلب بهصورت Service Layer یا UseCase پیادهسازی میشود که تعامل با شبکه، پایگاه داده و تحلیل را پنهان میکند. بر اساس Martin Fowler (Patterns of Enterprise Application Architecture, 2003)، Facade یکی از الگوهای کلیدی برای سازماندهی لایه سرویس است.
نکات اصلی
Facade — الگوی ساختاری که یک رابط یکپارچه به گروهی از رابطهای زیرسیستم ارائه میدهد. این الگو یک رابط سطح بالا تعریف میکند که استفاده از زیرسیستم را ساده میکند. Facade قابلیت جدیدی اضافه نمیکند — مؤلفههای موجود را ارکستر میکند و پیچیدگی تعامل آنها را از کلاینت پنهان میکند.
// زیرسیستم پیچیده
class AuthApi {
suspend fun login(email: String, pass: String): TokenResponse
}
class UserDao {
suspend fun saveUser(user: UserEntity)
suspend fun getUser(id: Long): UserEntity?
}
class AnalyticsTracker {
fun track(event: String, params: Map )
}
// Facade — رابط ساده برای UI
class AuthService(
private val api: AuthApi,
private val dao: UserDao,
private val analytics: AnalyticsTracker
) {
suspend fun loginUser(email: String, password: String): Result {
return runCatching {
val token = api.login(email, password)
val user = User(token.userId, email, token.accessToken)
dao.saveUser(user.toEntity())
analytics.track("login_success", mapOf("method" to "email"))
user
}
}
}AuthService — یک Facade که AuthApi، UserDao و AnalyticsTracker را از ViewModel پنهان میکند. UI بهجای سه درخواست جداگانه به API، پایگاه داده و تحلیل، loginUser(email, password) را فراخوانی میکند. این کار اتصال را کاهش میدهد: اگر فردا AuthApi به FirebaseAuth تبدیل شود یا UserDao به Room مهاجرت کند، فقط Facade تغییر میکند، نه UI.
Service Layer — پیادهسازی رایج Facade در برنامههای موبایل. این لایه منطق تجاری و هماهنگی بین لایهها را کپسوله میکند. در Android، Service Layer اغلب از طریق UseCase (Clean Architecture) پیادهسازی میشود و در iOS از طریق Manager یا پروتکلهای Service.
| مؤلفه | نقش در زیرسیستم | Facade چه چیزی را پنهان میکند |
|---|---|---|
| AuthApi | درخواست شبکه به سرور | فرمت درخواست، endpoint، مدیریت خطاهای HTTP |
| UserDao | ذخیرهسازی محلی توکن | طرح پایگاه داده، کوئریهای SQL، مهاجرتها |
| AnalyticsTracker | ارسال رویدادهای تحلیل | SDK Firebase/AppMetrica، فرمت رویدادها |
| NetworkMonitor | بررسی دسترسی به شبکه | ConnectivityManager، BroadcastReceiver |
AuthService هر چهار مؤلفه را ترکیب میکند. ViewModel یک متد را فراخوانی میکند بدون اینکه بداند زیر کاپوت یک درخواست شبکه، نوشتن در پایگاه داده، ردیابی و بررسی شبکه انجام میشود. هنگام تست، AuthService را میتوان با mock جایگزین کرد و کل منطق احراز هویت را بدون یکپارچهسازی با مؤلفههای واقعی بررسی کرد.
Facade، Adapter و Mediator — الگوهای ساختاری هستند اما مسائل مختلفی را حل میکنند. اغلب با هم اشتباه گرفته میشوند، چون هر سه یک شیء واسطه معرفی میکنند. تفاوتها را با مثال یک برنامه موبایل بررسی میکنیم.
| جنبه | Facade | Adapter | Mediator |
|---|---|---|---|
| هدف | سادهکردن رابط زیرسیستم | تبدیل رابط | کاهش اتصال مؤلفهها |
| جهت | یک رابط → زیرسیستم | کلاینت → Adaptee | N مؤلفه ↔ Mediator |
| تغییر رابط | جدید و سادهشده میسازد | موجود را تبدیل میکند | تغییر نمیدهد، هماهنگ میکند |
| آیا زیرسیستم از الگو خبر دارد؟ | خیر | خیر | بله، از طریق Mediator ارتباط برقرار میکند |
| نمونه در توسعه موبایل | UseCase / Service Layer | RecyclerView.Adapter | Coordinator در iOS |
Facade زیرسیستم را پنهان نمیکند — کلاینت در صورت نیاز میتواند مستقیماً به AuthApi دسترسی داشته باشد. Adapter حتماً رابط Adaptee را تغییر میدهد. Mediator تعاملات پیچیده بین بسیاری از اشیایی را که ممکن است از یکدیگر بیخبر باشند هماهنگ میکند.
پیادهسازی Facade در Kotlin برای Android با Clean Architecture از UseCase بهعنوان نقطه ورود برای هر سناریوی تجاری استفاده میکند. UseCase یک Facade است که مخزن، مپر و سایر وابستگیها را از لایه UI پنهان میکند.
// Repository — هم Facade است، اما یک سطح پایینتر
class UserRepositoryImpl(
private val local: UserLocalDataSource,
private val remote: UserRemoteDataSource,
private val mapper: UserMapper
) : UserRepository {
override suspend fun getUserProfile(id: String): UserProfile {
val cached = local.getUser(id)
if (cached != null && !cached.isStale) {
return mapper.toProfile(cached)
}
val dto = remote.fetchUser(id)
val entity = mapper.toEntity(dto)
local.saveUser(entity)
return mapper.toProfile(entity)
}
}
// UseCase — Facade برای سناریوی تجاری
class LoadUserProfileUseCase(
private val repo: UserRepository,
private val analytics: AnalyticsTracker
) {
suspend operator fun invoke(userId: String): Result {
return runCatching {
val profile = repo.getUserProfile(userId)
analytics.track("profile_loaded", mapOf("user_id" to userId))
profile
}
}
}LoadUserProfileUseCase — Facade برای سناریوی بارگذاری پروفایل. این الگو منطق کشکردن (local → remote)، نگاشت DTO → Entity → Profile و ردیابی تحلیل را پنهان میکند. ViewModel invoke(userId) را فراخوانی میکند و UserProfile آماده یا یک خطا دریافت میکند. UseCase را میتوان بهصورت ایزوله با جایگزینکردن مخزن با شیء mock تست کرد.
Facade در iOS اغلب بهصورت Manager یا Service پیادهسازی میشود. برخلاف Android، iOS برای تعریف رابط Facade از پروتکلها استفاده میکند که جایگزینی پیادهسازیها در تستها را آسان میکند. یک Facade برای کار با رسانه را بررسی میکنیم — بارگذاری، کشکردن و نمایش.
protocol MediaServiceProtocol {
func loadImage(from url: URL) async -> Result<UIImage, Error>
}
final class MediaService: MediaServiceProtocol {
private let cache: ImageCache
private let downloader: ImageDownloader
private let decoder: ImageDecoder
func loadImage(from url: URL) async -> Result<UIImage, Error> {
// ۱. بررسی کش
if let cached = cache.image(for: url) {
return .success(cached)
}
// ۲. بارگذاری داده
let result = await downloader.download(from: url)
guard case let .success(data) = result else {
return .failure(MediaError.downloadFailed)
}
// ۳. رمزگشایی
guard let image = decoder.decode(data) else {
return .failure(MediaError.decodeFailed)
}
// ۴. ذخیره در کش
cache.setImage(image, for: url)
return .success(image)
}
}MediaService فرآیند سهمرحلهای را کپسوله میکند: کش → بارگذاری → رمزگشایی. UI بهجای مدیریت ImageCache، URLSession و ImageDecoder یک متد loadImage(from:) را فراخوانی میکند. در تست میتوان MediaServiceProtocol را با mockی جایگزین کرد که تصاویر ازپیشتنظیمشده را بدون بارگذاری واقعی برمیگرداند.
اشتباهات در طراحی Facade مزایای آن را از بین میبرند: بهجای سادهسازی، God Object بهوجود میآید که کل سیستم به آن وابسته است. سه مشکل اصلی را بررسی میکنیم.
هنگامی که یک Facade حاوی متدهایی برای احراز هویت، بارگذاری پروفایل، ارسال پیام و همگامسازی است — این یک God Object است. نشانه: بیش از ۱۵ متد عمومی در یک کلاس. راهحل: تقسیم به چند Facade تخصصی بر اساس حوزههای مسئولیت — AuthService، ProfileService، MessagingService.
اگر Facade انواع خاص زیرسیستم را برمیگرداند (مثلاً FirebaseUser یا RealmObject)، کلاینت همچنان به یک پیادهسازی خاص وابسته است. راهحل: Facade باید فقط انواع خودش (data class / struct) را برگرداند و کلاینت را کاملاً از جزئیات زیرسیستم انتزاع کند.
هنگامی که Facade دسترسی مستقیم به زیرسیستم را ممنوع میکند، به تنگنا تبدیل میشود. گاهی کلاینت به یک متد خاص زیرسیستم نیاز دارد و مجبورکردن او به عبور از Facade زائد است. Facade نباید gatekeeper سختگیر باشد: رابط راحتی ارائه میدهد، اما دسترسی مستقیم به مؤلفهها را مسدود نمیکند.
سؤالات متداول
Facade رابط سادهشدهای به زیرسیستم ارائه میدهد و اغلب مجموعهای جدید از متدها میسازد. Proxy همان رابط شیء اصلی را حفظ میکند، اما کنترل دسترسی یا بارگذاری تنبل اضافه میکند. Facade برای سادهسازی است، Proxy برای کنترل.
Service Layer — پیادهسازی الگوی Facade در سطح معماری برنامه است. این لایه مرز بین UI و منطق تجاری را تعریف میکند و جزئیات پیادهسازی سرویسها را پنهان میکند. در Android، Service Layer اغلب از طریق UseCase و در iOS از طریق Manager یا پروتکلهای Service پیادهسازی میشود.
God Facade زمانی بهوجود میآید که یک کلاس مسئولیت چندین زیرسیستم نامرتبط را بر عهده میگیرد. نشانهها: بیش از ۱۵ متد عمومی، متدهایی از حوزههای مختلف (احراز هویت + پرداخت + اعلانها)، کلاسی که تست آن دشوار است (بیش از ۱۰ وابستگی). راهحل: تقسیم به Facadeهای دامنهای.
در برنامهای با ۱-۲ صفحه، Facade زائد است — فراخوانی مستقیم API و پایگاه داده از UI سادهتر و واضحتر است. Facade بهصرفه است با بیش از ۵ صفحه و بیش از ۳ زیرسیستم. در پروژههای متوسط و کوچک، Repository بهعنوان تنها لایه Facade بدون پوسته اضافی UseCase کافی است.
Facade تست را ساده میکند، زیرا کل زیرسیستم را با یک شیء mock جایگزین میکند. بهجای mock سه مؤلفه (شبکه + پایگاه داده + تحلیل)، mock یک Facade کافی است. در Swift برای این کار از protocol و در Kotlin از interface استفاده میشود. Facade برای تستهای یکپارچهسازی که ارکستراسیون مؤلفهها را بررسی میکنند نیز مناسب است.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید