Kompozycja a dziedziczenie
Część sekcji Programowanie obiektowe ścieżki Dart w Coddy. Lekcja 82 z 110.
Dowiedziałeś się, że dziedziczenie tworzy relację „jest” — Dog jest Animal. Istnieje jednak inne potężne podejście: kompozycja, która tworzy relację „ma”. Zamiast dziedziczyć zachowanie, klasa zawiera instancje innych klas i deleguje im pracę.
Rozważ klasę Car. Korzystając z dziedziczenia, możesz spróbować rozszerzyć klasę Engine — ale samochód nie jest silnikiem, tylko ma silnik:
class Engine {
void start() => print('Engine started');
}
class Car {
final Engine _engine = Engine(); // Kompozycja: Car MA Engine
void start() {
_engine.start(); // Deleguj do silnika
print('Car is ready to drive');
}
}
void main() {
var car = Car();
car.start();
}Kluczową zaletą kompozycji jest elastyczność. W przypadku dziedziczenia jesteś ograniczony do jednej klasy nadrzędnej. W przypadku kompozycji możesz łączyć wiele komponentów, a nawet zamieniać je w czasie działania programu:
class ElectricEngine {
void start() => print('Electric motor humming');
}
class HybridCar {
Engine? _gasEngine;
ElectricEngine? _electricEngine;
HybridCar({bool useElectric = false}) {
if (useElectric) {
_electricEngine = ElectricEngine();
} else {
_gasEngine = Engine();
}
}
}Popularna zasada brzmi: „przedkładaj kompozycję nad dziedziczenie”. Używaj dziedziczenia, gdy występuje rzeczywista relacja „jest czymś” i potrzebujesz polimorfizmu. Używaj kompozycji, gdy chcesz ponownie wykorzystać zachowanie bez ścisłego powiązania lub gdy obiekt potrzebuje możliwości z wielu źródeł.
Wyzwanie
ŁatwyZbudujmy system komputerowy z użyciem kompozycji! Zamiast próbować dziedziczyć po częściach komputera, utworzysz osobne klasy komponentów i połączysz je ze sobą — ponieważ komputer ma procesor CPU i ma pamięć, a nie jest procesorem CPU ani pamięcią.
Podzielisz kod na trzy pliki:
components.dart: Utwórz dwie klasy komponentów reprezentujące części komputera:- Klasę
CPUz polamiString brandidouble speedGHz. Dodaj metodęprocess(), która zwraca ciąg znaków:[brand] processing at [speedGHz]GHz - Klasę
Memoryz polemint sizeGB. Dodaj metodęload(), która zwraca ciąg znaków:Loading [sizeGB]GB of data
- Klasę
computer.dart: Utwórz klasęComputer, która łączy komponenty za pomocą kompozycji. Twój komputer powinien:- Mieć pole
String name - Zawierać instancję
CPUi instancjęMemory(kompozycja — komputer MA te części) - Przyjmować wszystkie niezbędne wartości za pomocą konstruktora, aby tworzyć oba komponenty wewnętrznie
- Mieć metodę
boot(), która deleguje zadania obu komponentom i wyświetla trzy wiersze: nazwę komputera, po której następujestarting..., następnie wynik metody process procesora CPU, a potem wynik metody load pamięci Memory - Mieć metodę
specs(), która zwraca ciąg znaków opisujący system:[name]: [brand] [speedGHz]GHz, [sizeGB]GB RAM
- Mieć pole
main.dart: Zaimportuj pliki i pokaż działanie kompozycji:- Utwórz obiekt
Computero nazwieWorkstationz procesorem CPUInteldziałającym z częstotliwością3.5GHz i pamięcią o pojemności16GB - Wywołaj metodę
boot() - Wyświetl wynik
specs()
- Utwórz obiekt
Zauważ, że klasa Computer nie dziedziczy po CPU ani Memory — zawiera je i deleguje im zadania. To relacja „ma” tworzona przez kompozycję!
Oczekiwany wynik:
Workstation starting...
Intel processing at 3.5GHz
Loading 16GB of data
Workstation: Intel 3.5GHz, 16GB RAMSpróbuj swoich sił
import 'computer.dart';
void main() {
// TODO: Utwórz obiekt Computer o nazwie "Workstation" z:
// - procesorem Intel działającym z częstotliwością 3.5 GHz
// - 16 GB pamięci
// TODO: Wywołaj metodę boot()
// TODO: Wypisz wynik specs()
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP
Pliki zewnętrzneBiblioteki i importyWprowadzenie do OOPKlasy a obiektySłowo kluczowe thisMetodyZmienne instancjiPodstawy konstruktorówPowtórka – prosty kalkulator4Bezpieczeństwo wartości null
Wprowadzenie do bezpieczeństwa wartości nullTypy dopuszczające null i niedopuszczające nullOperatory ? i !Słowo kluczowe late a bezpieczeństwo wartości nullOperatory uwzględniające nullBezpieczeństwo wartości null w klasachPodsumowanie — system profili użytkowników7Klasy abstrakcyjne i interfejsy
Klasy abstrakcyjneMetody abstrakcyjneInterfejsy w DartInterfejsy niejawneImplementowanie a rozszerzanieWiele interfejsówPowtórka — kalkulator kształtów10Kolekcje i typy generyczne
Przegląd List, Set i MapKolekcje bezpieczne typowoKlasy generyczneMetody generyczneOgraniczenia typów generycznychIterable i IteratorPowtórka — generyczne przechowywanie danych13Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieMetody rozszerzająceKlasy wywoływalneKlasy zapieczętowane (Dart 3)Rekordy (Dart 3)Wzorce i dopasowywanie (3.0)Typy wyliczeniowe z metodami16Projekt: Zarządzanie biblioteką
Omówienie projektuKlasy książek i użytkowników2Konstruktory w Dart
Konstruktor domyślnyKonstruktory nazwaneListy inicjalizująceKonstruktory stałeKonstruktory fabryczneKonstruktory przekierowującePodsumowanie — budowanie kształtów5Hermetyzacja
Elementy publiczne a prywatneKonwencja prefiksu _Prywatność na poziomie bibliotekiGettery i settery — szczegółowoUkrywanie informacjiPodsumowanie — dane uczniów8Domieszki
Wprowadzenie do domieszekTworzenie domieszekKorzystanie z wielu domieszekSłowo kluczowe on w domieszkachDomieszka a dziedziczenieDomieszka a interfejsPodsumowanie – system zwierząt11Metody specjalne
Nadpisanie toString()Nadpisanie hashCode i ==Interfejs ComparableMetoda call()Nadpisanie noSuchMethodPodsumowanie — własna kolekcja14Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy3Właściwości klas
Elementy instancji a elementy statycznePola final i constZmienne inicjalizowane z opóźnieniemMetody i pola statyczneGettery i setteryPowtórzenie – menedżer kont bankowych6Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe superPrzesłanianie metodAdnotacja @overrideSłowo kluczowe final dla klasKonstruktory i dziedziczeniePowtórzenie – hierarchia pracowników9Polimorfizm
Podstawy polimorfizmuPolimorfizm za pomocą interfejsówSprawdzanie typu w czasie działaniaOperatory is i asSłowo kluczowe covariantPodsumowanie — procesor płatnościPoćwicz samodzielnie: Kompilator Dart online