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 позива loginUser(email, password) уместо три одвојена захтева ка API, бази података и аналитици. То смањује спрегнутост: ако сутра 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 | Слање аналитичких догађаја | Firebase/AppMetrica SDK, формат догађаја |
| 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> {
// 1. Проверити кеш
if let cached = cache.image(for: url) {
return .success(cached)
}
// 2. Учитати податке
let result = await downloader.download(from: url)
guard case let .success(data) = result else {
return .failure(MediaError.downloadFailed)
}
// 3. Декодовати
guard let image = decoder.decode(data) else {
return .failure(MediaError.decodeFailed)
}
// 4. Сачувати у кеш
cache.setImage(image, for: url)
return .success(image)
}
}MediaService инкапсулира трокорачни процес: кеш → учитавање → декодовање. UI позива један метод loadImage(from:) уместо управљања ImageCache, URLSession и ImageDecoder. При тестирању се MediaServiceProtocol може заменити mock-ом који враћа унапред припремљене слике без стварног учитавања.
Грешке при пројектовању Facade поништавају његове предности: уместо поједностављења добија се God Object од кога зависи цео систем. Размотримо три главна проблема.
Када један Facade садржи методе за ауторизацију, учитавање профила, слање порука и синхронизацију — то је God Object. Ознака: 15+ јавних метода у једној класи. Решење: поделити на неколико специјализованих Facade према областима одговорности — AuthService, ProfileService, MessagingService.
Ако Facade враћа типове специфичне за подсистем (на пример, FirebaseUser или RealmObject), клијент је ипак везан за конкретну имплементацију. Решење: Facade треба да враћа само сопствене типове (data class / struct), потпуно апстрахујући клијента од детаља подсистема.
Када Facade забрањује директан приступ подсистему, постаје уско грло. Понекад клијенту је потребан специфичан метод подсистема, па је приморавати га да пролази кроз Facade сувишно. Facade не треба да буде строг чувар: он пружа згодан интерфејс, али не блокира директан приступ компонентама.
Често постављана питања
Facade пружа поједностављени интерфејс ка подсистему, често стварајући нови скуп метода. Proxy задржава исти интерфејс као оригинални објекат, али додаје контролу приступа или лењо учитавање. Facade — за поједностављење, Proxy — за контролу.
Service Layer — то је имплементација обрасца Facade на нивоу архитектуре апликације. Он дефинише границу између UI и пословне логике, скривајући детаље имплементације сервиса. На Android-у се Service Layer често реализује преко UseCase, на iOS-у — преко Manager или Service протокола.
God Facade настаје када једна класа преузме одговорност за неколико неповезаних подсистема. Ознаке: 15+ јавних метода, методе из различитих домена (ауторизација + плаћања + обавештења), класа коју је тешко тестирати (10+ зависности). Решење: поделити на доменске Facade.
У апликацији са 1-2 екрана Facade је сувишан — директан позив API и базе података из UI је једноставнији и јаснији. Facade се исплати при 5+ екрана и 3+ подсистема. У средњим и малим пројектима довољан је Repository као једини Facade слој, без додатног омотача UseCase.
Facade поједностављује тестирање, јер замењује цео подсистем једним mock објектом. Уместо mock три компоненте (мрежа + база + аналитика) довољно је mock једног Facade. На Swift-у се за то користи protocol, на Kotlin-у — interface. Facade је згодан и за интеграционе тестове, где се проверава оркестрација компоненти.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође