Menu
Coddy logo textTech

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ł.

challenge icon

Wyzwanie

Łatwy

Zbudujmy 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ę CPU z polami String brand i double speedGHz. Dodaj metodę process(), która zwraca ciąg znaków: [brand] processing at [speedGHz]GHz
    • Klasę Memory z polem int sizeGB. Dodaj metodę load(), która zwraca ciąg znaków: Loading [sizeGB]GB of data
  • computer.dart: Utwórz klasę Computer, która łączy komponenty za pomocą kompozycji. Twój komputer powinien:
    • Mieć pole String name
    • Zawierać instancję CPU i 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ępuje starting..., 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
  • main.dart: Zaimportuj pliki i pokaż działanie kompozycji:
    • Utwórz obiekt Computer o nazwie Workstation z procesorem CPU Intel działającym z częstotliwością 3.5 GHz i pamięcią o pojemności 16 GB
    • Wywołaj metodę boot()
    • Wyświetl wynik specs()

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 RAM

Spró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()
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Dart online