Facade είναι ένα δομικό πρότυπο σχεδίασης που παρέχει ένα απλοποιημένο περιβάλλον διεπαφής σε ένα πολύπλοκο υποσύστημα κλάσεων. Στην ανάπτυξη mobile, το 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 | Τοπική αποθήκευση του token | Το σχήμα της βάσης δεδομένων, τα ερωτήματα SQL, τις μεταναστεύσεις |
| AnalyticsTracker | Αποστολή γεγονότων αναλυτικών | Το SDK Firebase/AppMetrica, τη μορφή γεγονότων |
| NetworkMonitor | Έλεγχος διαθεσιμότητας δικτύου | ConnectivityManager, BroadcastReceiver |
AuthService συνδυάζει και τα τέσσερα στοιχεία. Το ViewModel καλεί μία μέθοδο, χωρίς να γνωρίζει ότι κάτω από το καπό γίνεται αίτημα δικτύου, εγγραφή στη βάση, παρακολούθηση και έλεγχος δικτύου. Κατά τον έλεγχο, το AuthService μπορεί να αντικατασταθεί με mock, ελέγχοντας όλη τη λογική αυθεντικοποίησης χωρίς ενσωμάτωση με πραγματικά στοιχεία.
Facade, Adapter και Mediator — δομικά πρότυπα, αλλά λύνουν διαφορετικά προβλήματα. Συχνά μπερδεύονται, καθώς και τα τρία εισάγουν ένα ενδιάμεσο αντικείμενο. Ας αναλύσουμε τις διαφορές με παράδειγμα μιας εφαρμογής κινητών.
| Πτυχή | Facade | Adapter | Mediator |
|---|---|---|---|
| Σκοπός | Απλοποίηση του περιβάλλοντος διεπαφής του υποσυστήματος | Μετατροπή του περιβάλλοντος | Μείωση της σύζευξης στοιχείων |
| Κατεύθυνση | Ένα περιβάλλον → υποσύστημα | Πελάτης → Adaptee | N στοιχεία ↔ Mediator |
| Αλλαγή του περιβάλλοντος | Δημιουργεί νέο, απλοποιημένο | Μετατρέπει το υπάρχον | Δεν αλλάζει, συντονίζει |
| Γνωρίζει το υποσύστημα το πρότυπο; | Όχι | Όχι | Ναι, επικοινωνεί μέσω Mediator |
| Παράδειγμα στην ανάπτυξη mobile | UseCase / Service Layer | RecyclerView.Adapter | Coordinator στο iOS |
Facade δεν κρύβει το υποσύστημα — ο πελάτης μπορεί να έχει άμεση πρόσβαση στο AuthApi όταν χρειάζεται. Adapter αλλάζει υποχρεωτικά το περιβάλλον του Adaptee. Mediator συντονίζει πολύπλοκες αλληλεπιδράσεις μεταξύ πολλών αντικειμένων που μπορεί να μην γνωρίζουν το ένα το άλλο.
Η υλοποίηση του Facade σε Kotlin για Android με Clean Architecture χρησιμοποιεί το UseCase ως σημείο εισόδου για κάθε επιχειρησιακό σενάριο. Το UseCase είναι ένα Facade που κρύβει το repository, τον mapper και άλλες εξαρτήσεις από το επίπεδο 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 μπορεί να ελεγχθεί μεμονωμένα, αντικαθιστώντας το repository με ένα αντικείμενο 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. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης