Facade är ett strukturellt designmönster som tillhandahåller ett förenklat gränssnitt till ett komplext delsystem av klasser. Inom mobilutveckling implementeras Facade oftast som Service Layer eller UseCase, som döljer interaktionen med nätverket, databasen och analysen. Enligt Martin Fowler (Patterns of Enterprise Application Architecture, 2003) är Facade ett av nyckelmönstren för att organisera servicelagret.
Huvudpunkter
Facade — strukturellt mönster som tillhandahåller ett enhetligt gränssnitt till en grupp gränssnitt i delsystemet. Det definierar ett gränssnitt på hög nivå som förenklar användningen av delsystemet. Facade lägger inte till ny funktionalitet — det orkestrerar befintliga komponenter och döljer komplexiteten i deras interaktion för klienten.
// Komplext delsystem
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 — enkelt gränssnitt för 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 som döljer AuthApi, UserDao och AnalyticsTracker för ViewModel. UI anropar loginUser(email, password) i stället för tre separata förfrågningar till API, databas och analys. Detta minskar kopplingen: om AuthApi i morgon blir FirebaseAuth eller UserDao migrerar till Room, ändras bara Facade, inte UI.
Service Layer — en vanlig implementering av Facade i mobilapplikationer. Det inkapslar affärslogiken och koordineringen mellan lager. I Android implementeras Service Layer ofta via UseCase (Clean Architecture), i iOS — via Manager eller Service-protokoll.
| Komponent | Roll i delsystemet | Vad Facade döljer |
|---|---|---|
| AuthApi | Nätverksförfrågan till servern | Begäranformat, endpoint, hantering av HTTP-fel |
| UserDao | Lokal lagring av token | Databasschema, SQL-frågor, migreringar |
| AnalyticsTracker | Skickande av analyshändelser | Firebase/AppMetrica SDK, händelseformat |
| NetworkMonitor | Kontroll av nätverkets tillgänglighet | ConnectivityManager, BroadcastReceiver |
AuthService förenar alla fyra komponenterna. ViewModel anropar en metod, utan att veta att det under huven sker en nätverksförfrågan, en skrivning till databasen, spårning och en nätverkskontroll. Vid testning kan AuthService ersättas med en mock och hela autentiseringslogiken kan kontrolleras utan integration med verkliga komponenter.
Facade, Adapter och Mediator — strukturella mönster, men de löser olika problem. De blandas ofta ihop, eftersom alla tre introducerar ett mellanliggande objekt. Låt oss analysera skillnaderna med hjälp av en mobilapplikation.
| Aspekt | Facade | Adapter | Mediator |
|---|---|---|---|
| Syfte | Förenkla delsystemets gränssnitt | Omvandla gränssnittet | Minska kopplingen mellan komponenter |
| Riktning | Ett gränssnitt → delsystem | Klient → Adaptee | N komponenter ↔ Mediator |
| Förändring av gränssnitt | Skapar ett nytt, förenklat | Omvandlar det befintliga | Ändrar inte, koordinerar |
| Känner delsystemet till mönstret? | Nej | Nej | Ja, kommunicerar via Mediator |
| Exempel inom mobilutveckling | UseCase / Service Layer | RecyclerView.Adapter | Coordinator i iOS |
Facade döljer inte delsystemet — klienten kan komma åt AuthApi direkt vid behov. Adapter ändrar obligatoriskt Adaptees gränssnitt. Mediator koordinerar komplexa interaktioner mellan många objekt som kanske inte känner till varandra.
Implementeringen av Facade i Kotlin för Android med Clean Architecture använder UseCase som ingångspunkt för varje affärsscenario. UseCase är en Facade som döljer repot, mappern och andra beroenden för UI-lagret.
// Repository — också Facade, men en nivå lägre
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 för affärsscenariot
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 för scenariot att ladda en profil. Det döljer cachningslogiken (local → remote), mappningen DTO → Entity → Profile och analysspårningen. ViewModel anropar invoke(userId) och får en färdig UserProfile eller ett fel. UseCase kan testas isolerat genom att ersätta repot med ett mock-objekt.
Facade i iOS implementeras ofta som Manager eller Service. Till skillnad från Android använder iOS protokoll för att definiera Facades gränssnitt, vilket gör det enkelt att byta ut implementeringar i tester. Låt oss titta på en Facade för att arbeta med media — laddning, cachning och visning.
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. Kontrollera cachen
if let cached = cache.image(for: url) {
return .success(cached)
}
// 2. Ladda data
let result = await downloader.download(from: url)
guard case let .success(data) = result else {
return .failure(MediaError.downloadFailed)
}
// 3. Avkoda
guard let image = decoder.decode(data) else {
return .failure(MediaError.decodeFailed)
}
// 4. Spara i cachen
cache.setImage(image, for: url)
return .success(image)
}
}MediaService inkapslar en process i tre steg: cache → laddning → avkodning. UI anropar en enda metod loadImage(from:) i stället för att hantera ImageCache, URLSession och ImageDecoder. Vid testning kan MediaServiceProtocol ersättas med en mock som returnerar förinställda bilder utan verklig laddning.
Misstagen vid utformningen av Facade upphäver dess fördelar: i stället för förenkling uppstår ett God Object som hela systemet är beroende av. Låt oss titta på tre huvudproblem.
När en enda Facade innehåller metoder för autentisering, profilladdning, meddelandesändning och synkronisering — är det ett God Object. Tecken: fler än 15 publika metoder i en klass. Lösning: dela upp i flera specialiserade Facade efter ansvarsområden — AuthService, ProfileService, MessagingService.
Om Facade returnerar typer som är specifika för delsystemet (till exempel FirebaseUser eller RealmObject), förblir klienten bunden till en konkret implementering. Lösning: Facade bör endast returnera egna typer (data class / struct), vilket helt abstraherar klienten från delsystemets detaljer.
När Facade förbjuder direkt åtkomst till delsystemet blir det en flaskhals. Ibland behöver klienten en specifik metod i delsystemet, och att tvinga den genom Facade är överflödigt. Facade behöver inte vara en strikt gatekeeper: det tillhandahåller ett bekvämt gränssnitt, men blockerar inte direkt åtkomst till komponenterna.
Vanliga frågor
Facade tillhandahåller ett förenklat gränssnitt till delsystemet och skapar ofta en ny uppsättning metoder. Proxy behåller samma gränssnitt som det ursprungliga objektet, men lägger till åtkomstkontroll eller lat inläsning. Facade — för förenkling, Proxy — för kontroll.
Service Layer — det är implementeringen av Facade-mönstret på arkitekturnivån i applikationen. Det definierar gränsen mellan UI och affärslogik och döljer detaljerna i tjänsternas implementering. I Android implementeras Service Layer ofta via UseCase, i iOS — via Manager eller Service-protokoll.
God Facade uppstår när en enda klass tar ansvar för flera orelaterade delsystem. Tecken: fler än 15 publika metoder, metoder från olika domäner (autentisering + betalningar + aviseringar), en klass som är svår att testa (fler än 10 beroenden). Lösning: dela upp i domänbaserade Facade.
I en applikation med 1-2 skärmar är Facade överflödigt — direktanrop av API och databas från UI är enklare och tydligare. Facade lönar sig vid fler än 5 skärmar och fler än 3 delsystem. I medelstora och små projekt räcker Repository som enda Facade-lager, utan ytterligare UseCase-omslag.
Facade förenklar testning, eftersom det ersätter hela delsystemet med ett enda mock-objekt. I stället för att mocka tre komponenter (nätverk + databas + analys) räcker det att mocka en enda Facade. I Swift används protocol för detta, i Kotlin — interface. Facade är också praktiskt för integrationstester, där orkestreringen av komponenter kontrolleras.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också