Το SoC (Separation of Concerns) είναι το ακρωνύμιο της αρχής σύμφωνα με την οποία ένα λογισμικό σύστημα χωρίζεται σε απομονωμένες περιοχές ευθύνης. Σύμφωνα με τον Martin Fowler, ο διαχωρισμός ευθυνών είναι βασικό στοιχείο του συντηρήσιμου κώδικα. Η αρχή SoC επιτρέπει στους προγραμματιστές να αλλάζουν ένα επίπεδο της εφαρμογής χωρίς να επηρεάζουν τα υπόλοιπα, κάτι που είναι ιδιαίτερα σημαντικό στην ομαδική ανάπτυξη εφαρμογών για κινητά.
Βασικά σημεία
SoC σημαίνει Separation of Concerns — «διαχωρισμός ευθυνών» ή «διαχωρισμός περιοχών ενδιαφέροντος». Στο πλαίσιο του προγραμματισμού, ο όρος concern αναφέρεται σε οποιαδήποτε διαχωρίσιμη λειτουργικότητα: εμφάνιση διεπαφής χρήστη, επεξεργασία κλικ, επικύρωση δεδομένων, δικτυακή επικοινωνία ή εργασία με βάση δεδομένων. Η αρχή SoC επιτάσσει την ομαδοποίηση κώδικα γύρω από αυτές τις περιοχές, έτσι ώστε οι αλλαγές στη μία να μην επηρεάζουν τις άλλες.
Το ακρωνύμιο SoC χρησιμοποιείται ευρέως στην τεχνική βιβλιογραφία, σε αρχιτεκτονικές συζητήσεις και στην τεκμηρίωση πλαισίων. Για παράδειγμα, στην τεκμηρίωση του Android Architecture Components, το SoC αναφέρεται επανειλημμένα ως κίνητρο για τον διαχωρισμό ViewModel και View. Στην κοινότητα iOS, ο όρος χρησιμοποιείται όταν συζητείται το πρόβλημα του Massive View Controller — άμεση συνέπεια της έλλειψης SoC.
Είναι σημαντικό να κατανοήσουμε ότι το SoC δεν είναι μια εφάπαξ ενέργεια, αλλά μια συνεχής διαδικασία. Καθώς η εφαρμογή μεγαλώνει, εμφανίζονται νέες περιοχές ευθύνης και η αρχιτεκτονική πρέπει να επανεξετάζεται. Μια καλή βάση κώδικα περνά από πολλές επαναλήψεις διαχωρισμού πριν φτάσει σε μια σταθερή κατάσταση όπου κάθε concern είναι απομονωμένο και διαχειρίσιμο.
Separation of Concerns και το ακρωνύμιό του SoC υποδηλώνουν την ίδια αρχή. Η διαφορά είναι μόνο στο πλαίσιο χρήσης: η πλήρης ονομασία χρησιμοποιείται σε επίσημα έγγραφα, εκπαιδευτικό υλικό και κατά την πρώτη εξήγηση της έννοιας σε νέους προγραμματιστές. Το SoC είναι βολικό σε τεχνικές συζητήσεις, ανασκοπήσεις κώδικα και τεκμηρίωση όπου η συντομία είναι σημαντική.
Σε επαγγελματικό περιβάλλον, και οι δύο όροι είναι εναλλάξιμοι. Ένας προγραμματιστής μπορεί να πει «εδώ παραβιάζεται το SoC» ή «αυτό παραβιάζει το Separation of Concerns» — το νόημα δεν αλλάζει. Ωστόσο, σε αγγελίες εργασίας και αρχιτεκτονικές απαιτήσεις εμφανίζεται συχνότερα η πλήρης ονομασία, ενώ σε συνομιλίες και ανασκοπήσεις κώδικα — το ακρωνύμιο. Η γνώση και των δύο παραλλαγών είναι απαραίτητη για μια άνετη είσοδο στον κλάδο.
Υπάρχει ορολογική σύγχυση: το ακρωνύμιο SoC χρησιμοποιείται επίσης σε περιβάλλον υλικού για System-on-a-Chip (σύστημα σε ένα τσιπ). Στην ανάπτυξη κινητών, το πλαίσιο είναι πάντα σαφές από το περιβάλλον — αν η συζήτηση αφορά την αρχιτεκτονική κώδικα, πρόκειται για Separation of Concerns. Σε αυτό το άρθρο, το SoC παντού αναφέρεται στην αρχή του διαχωρισμού ευθυνών.
Αρχιτεκτονική τριών επιπέδων — ο πιο συνηθισμένος τρόπος υλοποίησης του SoC σε εφαρμογές για κινητά. Χωρίζει τον κώδικα σε Presentation (UI), Domain (επιχειρηματική λογική) και Data (εργασία με πηγές). Κάθε επίπεδο περιέχει αυστηρά καθορισμένους τύπους κλάσεων και είναι απομονωμένο από τους γείτονες μέσω διεπαφών. Αυτή η προσέγγιση είναι εξίσου αποτελεσματική για έργα iOS, Android και Flutter.
View και ViewModel αποτελούν το επίπεδο παρουσίασης. Το View είναι υπεύθυνο για την απόδοση της διεπαφής και τη μετάδοση γεγονότων χρήστη. Το ViewModel αποθηκεύει την κατάσταση της οθόνης και μετατρέπει τα δεδομένα από το επίπεδο Domain σε μορφή έτοιμη για εμφάνιση. Το ViewModel δεν έχει αναφορές σε Activity, Fragment ή UIViewController — αυτό εξασφαλίζει SoC μεταξύ UI και λογικής.
Για παράδειγμα, στο Android Jetpack το ViewModel επιβιώνει από την περιστροφή οθόνης, ενώ το UI δημιουργείται ξανά. Χωρίς SoC θα έπρεπε να αποθηκεύουμε την κατάσταση στο Activity, αναμειγνύοντας τη διαχείριση κύκλου ζωής με δεδομένα. Το ViewModel λύνει αυτό το έργο απομονωμένα, επιδεικνύοντας μια καθαρή υλοποίηση της αρχής διαχωρισμού ευθυνών.
Use Cases περιέχουν επιχειρηματικούς κανόνες ανεξάρτητους από πλατφόρμα. Αυτό το επίπεδο δεν εισάγει Android SDK, iOS UIKit ή Flutter framework. Το Use Case λαμβάνει δεδομένα από το Repository, εφαρμόζει σε αυτά επιχειρηματική λογική και επιστρέφει το αποτέλεσμα. Χάρη στο SoC, ένα Use Case μπορεί να επαναχρησιμοποιηθεί σε διαφορετικές οθόνες και πλατφόρμες.
Ένα κλασικό παράδειγμα — ValidateAndSaveUseCase για φόρμα εγγραφής. Ελέγχει την ορθότητα email και κωδικού πρόσβασης, καλεί το UserRepository για αποθήκευση και επιστρέφει ValidationResult. Ούτε το UI ούτε η βάση δεδομένων γνωρίζουν τους κανόνες επικύρωσης — είναι συγκεντρωμένοι σε ένα μέρος, γεγονός που απλοποιεί την αλλαγή τους.
Repository αφαιρεί τις πηγές δεδομένων από την υπόλοιπη εφαρμογή. Το ViewModel δεν γνωρίζει από πού προέρχονται τα δεδομένα — REST API, GraphQL, τοπική βάση δεδομένων ή προσωρινή μνήμη. Το Repository αποφασίζει ποια πηγή να χρησιμοποιήσει και κρύβει αυτή τη λογική πίσω από μια διεπαφή. Αυτό είναι SoC μεταξύ λήψης δεδομένων και κατανάλωσής τους.
Το DataSource παρέχει ακόμη βαθύτερο διαχωρισμό: το RemoteDataSource είναι υπεύθυνο μόνο για αιτήματα HTTP, το LocalDataSource — για εργασία με Room, CoreData ή SharedPreferences. Το Repository τα συνδυάζει εφαρμόζοντας στρατηγικές προσωρινής αποθήκευσης. Κάθε DataSource μπορεί να αντικατασταθεί ανεξάρτητα, κάτι που είναι κρίσιμο κατά τη μετεγκατάσταση μεταξύ διακομιστών ή βάσεων δεδομένων.
Ένα τέτοιο πολυεπίπεδο σύστημα DataSource υλοποιεί το SoC σε επίπεδο υποδομής: δικτυακή επικοινωνία, τοπική αποθήκευση και προσωρινή μνήμη — ξεχωριστά concerns, το καθένα με τη δική του λογική και κύκλο ζωής. Κατά την αντικατάσταση του πελάτη HTTP, αλλάζει μόνο το RemoteDataSource, ενώ το Repository και τα ανώτερα επίπεδα παραμένουν ανέπαφα, επιβεβαιώνοντας την πρακτική αξία του διαχωρισμού ευθυνών.
MVP (Model-View-Presenter) — ένα από τα πρώτα πρότυπα που υλοποιούν ρητά το SoC στην ανάπτυξη κινητών. Το Presenter περιέχει τη λογική και διαχειρίζεται το View μέσω μιας διεπαφής. Το View είναι παθητικό — εμφανίζει μόνο ό,τι λέει το Presenter. Ο διαχωρισμός απλοποιεί τη δοκιμή: το Presenter δοκιμάζεται χωρίς εξομοιωτή, ενώ το View παραμένει τόσο απλό που δεν υπάρχει τίποτα να χαλάσει.
MVVM πρόσθεσε τη reactive σύνδεση: το View εγγράφεται στις αλλαγές του ViewModel μέσω Observable ή StateFlow. Το ViewModel δεν αποθηκεύει αναφορά στο View, εξαλείφοντας τον κίνδυνο διαρροής μνήμης και διαχωρίζοντας ακόμη πιο έντονα τα concerns. Στο Android, το MVVM έγινε πρότυπο χάρη στο Jetpack ViewModel και LiveData, στο iOS — χάρη στο Combine και RxSwift.
Clean Architecture του Robert Martin φέρνει το SoC σε ριζικό διαχωρισμό σε δακτυλίους. Ο εξωτερικός δακτύλιος (πλαίσια και οδηγοί) εξαρτάται από τον εσωτερικό (οντότητες), αλλά όχι το αντίστροφο. Στην πράξη, τα έργα κινητών σπάνια υλοποιούν και τους τέσσερις δακτυλίους — τα επίπεδα Domain και Data γύρω από το Presentation είναι αρκετά. Αλλά η ίδια η αρχή της εξάρτησης «προς τα μέσα» παρέχει σημαντικά πλεονεκτήματα κατά την αλλαγή πλαισίων.
// View — μόνο εμφάνιση, χωρίς λογική
final class LoginViewController: UIViewController {
let viewModel: LoginViewModel
func loginTapped() {
viewModel.login(emailField.text, passwordField.text)
}
}
// ViewModel — περιέχει λογική οθόνης, δεν γνωρίζει UIKit
final class LoginViewModel {
private let loginUseCase: LoginUseCase
func login(email: String?, password: String?) {
loginUseCase.execute(email, password)
}
}
// Use Case — επιχειρηματική λογική, ανεξάρτητη από πλατφόρμα
final class LoginUseCase {
private let repo: AuthRepository
func execute(email: String?, password: String?) {
guard let e = email, let p = password else { return }
repo.authenticate(e, p)
}
}
Το παράδειγμα δείχνει τρία επίπεδα SoC: το LoginViewController απλώς μεταδίδει γεγονότα, το LoginViewModel διαχειρίζεται την κατάσταση, το LoginUseCase περιέχει επιχειρηματικούς κανόνες. Κάθε κλάση δοκιμάζεται ανεξάρτητα και η αλλαγή του πλαισίου UI δεν επηρεάζει το Use Case.
Massive View Controller — η πιο συχνή παραβίαση του SoC στο iOS. Μια κλάση που διαχειρίζεται το UI, επεξεργάζεται αιτήματα δικτύου, αναλύει JSON και αποθηκεύει δεδομένα, παραβιάζει την αρχή σε όλα τα επίπεδα. Λύση — εξάγετε κάθε ευθύνη σε ξεχωριστό στοιχείο: NetworkingService, JSONParser, CoreDataStack, αφήνοντας στο ViewController μόνο τη διαχείριση του View.
Στο Android υπάρχει ανάλογο πρόβλημα — God Activity ή God Fragment. Μία δραστηριότητα που φορτώνει δεδομένα, επικυρώνει φόρμες, εμφανίζει διαλόγους και ενημερώνει το UI. Θεραπεύεται με την εισαγωγή ViewModel και Repository, που αναλαμβάνουν τη διαχείριση κατάστασης και δεδομένων. Το ViewModel προστατεύει επίσης από απώλεια δεδομένων κατά την περιστροφή οθόνης.
Η τρίτη παραβίαση — ανάμειξη πλατφορμικού και επιχειρηματικού κώδικα. Για παράδειγμα, τοποθέτηση ενός αιτήματος HTTP απευθείας σε SwiftUI View ή Android Composable. Αυτό καθιστά τον κώδικα μη φορητό και δύσκολο στη δοκιμή. Η σωστή προσέγγιση — εξαγωγή του αιτήματος στο Repository, το οποίο καλείται μέσω Use Case, και το View απλώς εγγράφεται στο αποτέλεσμα. Κάθε στοιχείο του συστήματος λύνει τη δική του εργασία και δεν υπερβαίνει τα όριά του.
Συχνές Ερωτήσεις
Όχι. Το SoC είναι μια γενικότερη αρχή διαχωρισμού του συστήματος σε περιοχές ευθύνης. Το SOLID είναι ένα σύνολο πέντε συγκεκριμένων κανόνων για αντικειμενοστραφή σχεδίαση. Η πρώτη αρχή SOLID (Single Responsibility) είναι μια ειδική περίπτωση του SoC σε επίπεδο μίας κλάσης.
Χρησιμοποιήστε τον κανόνα ενός λόγου για αλλαγή (Single Responsibility). Αν μια κλάση αλλάζει λόγω αλλαγής UI, μορφής δεδομένων και επιχειρηματικών κανόνων — το SoC παραβιάζεται. Εργαλεία όπως το ArchTest (Android) και το StrictConcurrency (iOS) βοηθούν στον αυτόματο εντοπισμό τέτοιων παραβιάσεων.
Στη θεωρία, τα επιπλέον επίπεδα προσθέτουν έμμεσες κλήσεις, αλλά στην πράξη η επίδραση στην απόδοση της εφαρμογής για κινητά είναι αμελητέα. Ο μεταγλωττιστής κάνει inline πολλές κλήσεις και οι βελτιστοποιήσεις JIT και AOT εξαλείφουν την επιβάρυνση. Η συντηρησιμότητα του κώδικα κερδίζει πολύ περισσότερα από όσα χάνονται σε αφαιρέσεις.
Ξεκινήστε με εξαγωγή των αιτημάτων δικτύου από το UI στο Repository. Στη συνέχεια, διαχωρίστε την επιχειρηματική λογική σε Use Cases. Χρησιμοποιήστε dependency injection για τη σύνδεση επιπέδων. Κάντε αλλαγές επαναληπτικά, καλύπτοντας τον νέο κώδικα με δοκιμές — αυτό εγγυάται ότι η αναδόμηση δεν θα σπάσει την υπάρχουσα λειτουργικότητα.
Σε πρωτότυπα μπορείτε να παραβιάσετε το SoC για ταχύτητα. Αλλά αν το πρωτότυπο περάσει σε ανάπτυξη προϊόντος, το κόστος αναδόμησης μπορεί να υπερβεί το όφελος της γρήγορης εκκίνησης. Βέλτιστα — διατηρήστε ελάχιστο διαχωρισμό (UI και δεδομένα) ακόμη και στο πρωτότυπο, για να μην χρειαστεί να ξαναγράψετε τα πάντα από την αρχή κατά την έναρξη.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης