Menu
Coddy logo textTech

Kompozycja a dziedziczenie

Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 47 z 87.

Dziedziczenie tworzy relację „jest”: Dog jest Animal. Ale co, jeśli klasa potrzebuje funkcjonalności czegoś, czym nie jest? Właśnie tu wkracza kompozycja, ustanawiając zamiast tego relację „ma”.

Przy kompozycji klasa zawiera instancje innych klas jako pola, zamiast po nich dziedziczyć:

// Podejście oparte na dziedziczeniu: Car JEST Engine (nie ma sensu)
class Car extends Engine { }

// Podejście oparte na kompozycji: Car MA Engine (ma sens)
class Car {
    private Engine engine;
    
    public Car(Engine engine) {
        this.engine = engine;
    }
    
    public void start() {
        engine.ignite();
    }
}

Kompozycja oferuje kilka zalet. Możesz zmienić skomponowany obiekt w czasie działania, na przykład wymieniając silniki.

Możesz także łączyć zachowania z wielu klas, na co dziedziczenie nie pozwala w Javie. Skomponowana klasa udostępnia tylko wybrane przez siebie metody, zapewniając lepszą enkapsulację.

Praktyczna wskazówka: używaj dziedziczenia, gdy zachodzi rzeczywista relacja „jest czymś” i podklasa faktycznie stanowi wyspecjalizowaną wersję klasy nadrzędnej.

Używaj kompozycji, gdy potrzebujesz skorzystać z funkcjonalności innej klasy, nie będąc jednocześnie jej typem. Wielu doświadczonych programistów kieruje się zasadą: „przedkładaj kompozycję nad dziedziczenie”, ponieważ prowadzi to do bardziej elastycznego i łatwiejszego w utrzymaniu kodu.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system komputerowy z użyciem kompozycji, aby zademonstrować relację "has-a". Zamiast dziedziczyć klasę Computer po jej komponentach (co nie miałoby sensu), komputer będzie zawierać części potrzebne do działania.

Podzielisz kod na trzy pliki:

  • Processor.java: Utwórz klasę reprezentującą procesor CPU. Klasa Processor ma dwa prywatne pola: brand (String) i speedGHz (double). Dodaj konstruktor inicjalizujący oba pola oraz metody pobierające wartość każdego z nich. Dodaj metodę process(), która zwraca String: [brand] processing at [speedGHz] GHz
  • Memory.java: Utwórz klasę reprezentującą pamięć RAM. Klasa Memory ma dwa prywatne pola: type (String, na przykład "DDR4" lub "DDR5") i sizeGB (int). Dodaj konstruktor, metody pobierające wartości pól oraz metodę load(), która zwraca: Loading [sizeGB]GB [type] memory
  • Computer.java: To właśnie tutaj kompozycja pokazuje swoje zalety! Klasa Computer powinna mieć obiekty Processor i Memory jako prywatne pola. Nie rozszerza ich, lecz je zawiera. Dodaj konstruktor przyjmujący oba komponenty. Dodaj metodę boot(), która zwraca wielowierszowy String pokazujący uruchamianie komputera z użyciem jego komponentów:
    Booting computer...
    [processor.process() result]
    [memory.load() result]
    System ready!
    Dodaj również metodę getSpecs(), która zwraca: Specs: [processor brand] CPU, [memory sizeGB]GB [memory type]
  • Main.java: Uruchom swój złożony system! Otrzymasz cztery dane wejściowe: markę procesora (String), szybkość procesora (double), typ pamięci (String) i rozmiar pamięci (int).

    Utwórz obiekty Processor i Memory z tymi wartościami, a następnie połącz je w obiekt Computer. Wypisz wynik wywołania boot(), a potem wypisz wynik wywołania getSpecs().

Otrzymasz kolejno cztery dane wejściowe: markę procesora, szybkość procesora (GHz), typ pamięci i rozmiar pamięci (GB).

Zwróć uwagę, jak Computer deleguje zadania swoim komponentom, zamiast próbować nimi być. Na tym polega siła kompozycji! Computer może korzystać z dowolnego obiektu Processor lub Memory, który mu przekażesz, dzięki czemu projekt jest elastyczny i realistyczny.

Spróbuj swoich sił

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Odczytaj dane wejściowe
        String processorBrand = scanner.nextLine();
        double processorSpeed = Double.parseDouble(scanner.nextLine());
        String memoryType = scanner.nextLine();
        int memorySize = Integer.parseInt(scanner.nextLine());
        
        // TODO: Utwórz obiekt Processor z podaną marką i prędkością
        
        // TODO: Utwórz obiekt Memory z podanym typem i rozmiarem
        
        // TODO: Utwórz obiekt Computer, łącząc obiekty Processor i Memory
        
        // TODO: Wypisz wynik boot()
        
        // TODO: Wypisz wynik getSpecs()
    }
}
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 Java online