Facade egy strukturális tervezési minta, amely egyszerűsített interfészt biztosít az osztályok összetett alrendszeréhez. A mobilfejlesztésben a Facade-et leggyakrabban Service Layer vagy UseCase formájában valósítják meg, elrejtve a hálózattal, az adatbázissal és az analitikával való interakciót. Martin Fowler szerint (Patterns of Enterprise Application Architecture, 2003) a Facade az egyik kulcsfontosságú minta a szolgáltatási réteg szervezéséhez.
Főbb pontok
Facade — strukturális minta, amely egységes interfészt biztosít az alrendszer interfészeinek csoportjához. Magas szintű interfészt határoz meg, amely egyszerűsíti az alrendszer használatát. A Facade nem ad hozzá új funkcionalitást — a meglévő komponenseket orkesztrálja, elrejtve azok interakciójának összetettségét az ügyfél elől.
// Összetett alrendszer
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 — egyszerű interfész a UI számára
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, amely az AuthApi-t, a UserDao-t és az AnalyticsTracker-t elrejti a ViewModel elől. A UI a loginUser(email, password) metódust hívja a három külön API-, adatbázis- és analitika-kérés helyett. Ez csökkenti a csatolást: ha holnap az AuthApi FirebaseAuth lesz, vagy a UserDao Room-ra migrál, csak a Facade változik, a UI nem.
Service Layer — a Facade elterjedt megvalósítása a mobilalkalmazásokban. Beágyazza az üzleti logikát és a rétegek közötti koordinációt. Androidon a Service Layer gyakran UseCase-en (Clean Architecture), iOS-en pedig Manager vagy Service protokollokon keresztül valósul meg.
| Komponens | Szerep az alrendszerben | Mit rejt el a Facade |
|---|---|---|
| AuthApi | Hálózati kérés a szerverhez | A kérés formátumát, az endpointot, a HTTP-hibák kezelését |
| UserDao | A token helyi tárolása | Az adatbázis-sémát, az SQL-lekérdezéseket, a migrációkat |
| AnalyticsTracker | Analitikai események küldése | A Firebase/AppMetrica SDK-t, az események formátumát |
| NetworkMonitor | A hálózat elérhetőségének ellenőrzése | ConnectivityManager, BroadcastReceiver |
AuthService mind a négy komponenst egyesíti. A ViewModel egyetlen metódust hív, anélkül hogy tudná, hogy a motorháztető alatt hálózati kérés, adatbázisba írás, nyomkövetés és hálózatellenőrzés zajlik. Teszteléskor az AuthService mockkal helyettesíthető, és a teljes hitelesítési logika ellenőrizhető a valódi komponensekkel való integráció nélkül.
Facade, Adapter és Mediator — strukturális minták, de különböző feladatokat oldanak meg. Gyakran összekeverik őket, mivel mindhárom közvetítő objektumot vezet be. Vizsgáljuk meg a különbségeket egy mobilalkalmazás példáján.
| Szempont | Facade | Adapter | Mediator |
|---|---|---|---|
| Cél | Az alrendszer interfészének egyszerűsítése | Az interfész átalakítása | A komponensek csatolásának csökkentése |
| Irány | Egy interfész → alrendszer | Ügyfél → Adaptee | N komponens ↔ Mediator |
| Az interfész változása | Újat, egyszerűsítettet hoz létre | A meglévőt alakítja át | Nem változtat, koordinál |
| Tud-e az alrendszer a mintáról? | Nem | Nem | Igen, a Mediatoron keresztül kommunikál |
| Példa a mobilfejlesztésben | UseCase / Service Layer | RecyclerView.Adapter | Coordinator iOS-ben |
Facade nem rejti el az alrendszert — az ügyfél szükség esetén közvetlenül hozzáférhet az AuthApi-hoz. Adapter kötelezően megváltoztatja az Adaptee interfészét. Mediator számos, egymást esetleg nem ismerő objektum közötti összetett interakciókat koordinál.
A Facade megvalósítása Kotlinban Androidhoz a Clean Architecture-szel a UseCase-t használja belépési pontként minden üzleti forgatókönyvhöz. A UseCase egy Facade, amely a repositoryt, a mappert és más függőségeket elrejti a UI-réteg elől.
// Repository — szintén Facade, de egy szinttel lejjebb
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 az üzleti forgatókönyvhöz
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 a profilbetöltési forgatókönyvhöz. Elrejti a gyorsítótárazási logikát (local → remote), a DTO → Entity → Profile leképezést és az analitikai nyomkövetést. A ViewModel az invoke(userId) metódust hívja, és kész UserProfile-ot vagy hibát kap. A UseCase izoláltan tesztelhető, ha a repositoryt mock objektumra cseréljük.
Facade iOS-ben gyakran Manager vagy Service formájában valósul meg. Az Androiddal ellentétben az iOS protokollokat használ a Facade interfészének meghatározására, ami megkönnyíti a megvalósítások cseréjét a tesztekben. Vizsgáljuk meg a médiával való munkához készült Facade-et — betöltés, gyorsítótárazás és megjelenítés.
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> {
// 1. Gyorsítótár ellenőrzése
if let cached = cache.image(for: url) {
return .success(cached)
}
// 2. Adatok betöltése
let result = await downloader.download(from: url)
guard case let .success(data) = result else {
return .failure(MediaError.downloadFailed)
}
// 3. Dekódolás
guard let image = decoder.decode(data) else {
return .failure(MediaError.decodeFailed)
}
// 4. Mentés a gyorsítótárba
cache.setImage(image, for: url)
return .success(image)
}
}MediaService egy háromlépéses folyamatot ágyaz be: gyorsítótár → betöltés → dekódolás. A UI egyetlen loadImage(from:) metódust hív az ImageCache, az URLSession és az ImageDecoder kezelése helyett. Teszteléskor a MediaServiceProtocol mockra cserélhető, amely valódi betöltés nélkül előre beállított képeket ad vissza.
A hibák a Facade tervezésekor semmissé teszik annak előnyeit: az egyszerűsítés helyett God Object jön létre, amelytől az egész rendszer függ. Vizsgáljuk meg a három fő problémát.
Amikor egy Facade hitelesítési, profilbetöltési, üzenetküldési és szinkronizálási metódusokat tartalmaz — ez egy God Object. Jele: 15+ nyilvános metódus egyetlen osztályban. Megoldás: osszuk szét több specializált Facade-re a felelősségi területek szerint — AuthService, ProfileService, MessagingService.
Ha a Facade az alrendszerre jellemző típusokat ad vissza (például FirebaseUser vagy RealmObject), az ügyfél akkor is egy konkrét megvalósításhoz kötődik. Megoldás: a Facade-nek csak saját típusokat (data class / struct) szabad visszaadnia, teljesen elvonatkoztatva az ügyfelet az alrendszer részleteitől.
Amikor a Facade megtiltja a közvetlen hozzáférést az alrendszerhez, szűk keresztmetszetté válik. Néha az ügyfélnek az alrendszer egy speciális metódusára van szüksége, és arra kényszeríteni, hogy a Facade-en keresztül menjen, felesleges. A Facade ne legyen szigorú kapuőr: kényelmes interfészt biztosít, de nem blokkolja a közvetlen hozzáférést a komponensekhez.
Gyakran ismételt kérdések
Facade egyszerűsített interfészt biztosít az alrendszerhez, gyakran új metóduskészletet hozva létre. Proxy megtartja az eredeti objektummal azonos interfészt, de hozzáférés-ellenőrzést vagy lusta betöltést ad hozzá. Facade — az egyszerűsítéshez, Proxy — az ellenőrzéshez.
Service Layer — a Facade minta megvalósítása az alkalmazás architektúrájának szintjén. Meghatározza a UI és az üzleti logika közötti határt, elrejtve a szolgáltatások megvalósításának részleteit. Androidon a Service Layer gyakran UseCase-en, iOS-en pedig Manager vagy Service protokollokon keresztül valósul meg.
God Facade akkor jön létre, amikor egyetlen osztály több, egymással nem összefüggő alrendszerért vállal felelősséget. Jelek: 15+ nyilvános metódus, különböző tartományokból származó metódusok (hitelesítés + fizetések + értesítések), nehezen tesztelhető osztály (10+ függőség). Megoldás: osszuk szét tartományi Facade-ekre.
Az 1-2 képernyős alkalmazásban a Facade felesleges — az API és az adatbázis közvetlen hívása a UI-ból egyszerűbb és érthetőbb. A Facade megtérül 5+ képernyő és 3+ alrendszer esetén. A közepes és kis projektekben elegendő a Repository mint egyetlen Facade-réteg, további UseCase-burkolat nélkül.
A Facade leegyszerűsíti a tesztelést, mivel a teljes alrendszert egyetlen mock objektummal helyettesíti. Három komponens (hálózat + adatbázis + analitika) mockolása helyett elegendő egyetlen Facade mockolása. Swiftben ehhez protocolt, Kotlinban interfacet használnak. A Facade az integrációs tesztekhez is kényelmes, ahol a komponensek orkesztrációját ellenőrzik.
Összegzés
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is