Sobes.tech
Middle

Τι μπορείς να μου πεις για την αρχή D στο SOLID;

sobes.tech AI

Απάντηση από AI

Η αρχή της αντιστροφής των εξαρτήσεων (Dependency Inversion Principle) δηλώνει:

  • Τα modules υψηλότερου επιπέδου δεν πρέπει να εξαρτώνται από τα modules χαμηλότερου επιπέδου. Και τα δύο είδη modules πρέπει να εξαρτώνται από αφαιρέσεις.
  • Οι αφαιρέσεις δεν πρέπει να εξαρτώνται από λεπτομέρειες. Οι λεπτομέρειες πρέπει να εξαρτώνται από αφαιρέσεις.

Ο στόχος του D στο SOLID είναι να αποσυνδέσει (decoupling) τις πολιτικές υψηλού επιπέδου από την υλοποίηση χαμηλού επιπέδου. Αντί να εξαρτάται ένα module υψηλού επιπέδου άμεσα από την συγκεκριμένη υλοποίηση ενός module χαμηλού επιπέδου, και τα δύο εξαρτώνται από μια κοινή διεπαφή ή μια αφηρημένη κλάση. Αυτό αυξάνει την ευελιξία, την δοκιμαστικότητα και τη διαχειρισιμότητα του κώδικα.

Στο Flutter, αυτό συχνά εκδηλώνεται μέσω της χρήσης αφηρημένων κλάσεων ή mixins για τον ορισμό συμβολαίων, που στη συνέχεια υλοποιούνται από συγκεκριμένες κλάσεις. Για παράδειγμα, όταν δουλεύουμε με δεδομένα:

// Αφηρημένη κλάση
abstract class DataService {
  Future<List<String>> getData();
}

// Χαμηλού επιπέδου υλοποίηση (συγκεκριμένη λεπτομέρεια)
class ApiDataService implements DataService {
  @override
  Future<List<String>> getData() async {
    // Λογική λήψη δεδομένων από API
    return ['API Data 1', 'API Data 2'];
  }
}

// Άλλη χαμηλού επιπέδου υλοποίηση (άλλη λεπτομέρεια)
class LocalDataService implements DataService {
  @override
  Future<List<String>> getData() async {
    // Λογική λήψη δεδομένων από τοπική αποθήκευση
    return ['Local Data 1', 'Local Data 2'];
  }
}

// Υψηλού επιπέδου module που εξαρτάται από την αφαιρεση, όχι από λεπτομέρειες
class DataManager {
  final DataService _dataService; // Εξάρτηση από την αφαιρεση

  DataManager(this._dataService); // Η εξάρτηση εισάγεται (inversion)

  Future<List<String>> fetchData() {
    return _dataService.getData();
  }
}

// Χρήση
void main() {
  final apiService = ApiDataService();
  final localService = LocalDataService();

  final apiManager = DataManager(apiService); // Εισαγωγή ApiDataService
  final localManager = DataManager(localService); // Εισαγωγή LocalDataService

  // ... χρήση των apiManager ή localManager
}

Σε αυτό το παράδειγμα, το DataManager (υψηλού επιπέδου module) δεν εξαρτάται άμεσα από το ApiDataService ή το LocalDataService. Εξαρτάται από την αφαιρεση DataService. Η συγκεκριμένη υλοποίηση του DataService "εισάγεται" στο DataManager από έξω, που αποτελεί μια έκφραση της αντιστροφής των εξαρτήσεων. Αυτό επιτρέπει την εύκολη αντικατάσταση των πηγών δεδομένων χωρίς να αλλάξει η λογική του DataManager.