Kompozycja
Część sekcji Programowanie obiektowe ścieżki Rust w Coddy. Lekcja 55 z 61.
Wiele języków obiektowych w dużym stopniu opiera się na dziedziczeniu, aby współdzielić funkcjonalność między typami. Rust stosuje inne podejście: kompozycję. Zamiast dziedziczyć zachowanie po typie nadrzędnym, budujesz złożone typy, uwzględniając inne typy jako pola.
Rozważ modelowanie samochodu. Zamiast tworzyć bazową klasę Vehicle i po niej dziedziczyć, składasz Car z mniejszych, wyspecjalizowanych komponentów:
struct Engine {
horsepower: u32,
}
struct Wheels {
count: u8,
diameter: f32,
}
struct Car {
engine: Engine,
wheels: Wheels,
brand: String,
}
Car nie dziedziczy po Engine ani Wheels. Ma je. Ta relacja typu „ma” to kompozycja. Możesz uzyskać dostęp do wewnętrznych komponentów przez ich pola i bezpośrednio wywoływać ich metody:
impl Engine {
fn start(&self) {
println!("Engine with {} HP started!", self.horsepower);
}
}
impl Car {
fn start(&self) {
self.engine.start(); // Deleguj do komponentu wewnętrznego
}
}
Ten wzorzec pozwala skupić każdą strukturę na jednym zadaniu. Chcesz zmienić sposób działania silników? Zmodyfikuj tylko strukturę Engine. Chcesz użyć ponownie Wheels w motocyklu? Po prostu uwzględnij ją w nowej strukturze. Kompozycja zapewnia elastyczność bez ścisłego powiązania, które często tworzy dziedziczenie.
Wyzwanie
ŁatwyZbudujmy system komputerowy z użyciem kompozycji! Zamiast tworzyć jedną ogromną strukturę, złożysz Computer z mniejszych, wyspecjalizowanych komponentów: CPU i Memory. Każdy komponent będzie wiedział, jak opisać samego siebie, a Computer przekaże zadanie swoim częściom.
Rozmieścisz kod w trzech plikach:
cpu.rs: Zdefiniuj publiczną strukturęCPUz dwoma publicznymi polami:cores(u32) określającym liczbę rdzeni orazspeed_ghz(f32) określającym częstotliwość taktowania. Dodaj metodęspecs, która zwraca String opisujący CPU w formacie{cores}-core @ {speed_ghz}GHz.memory.rs: Zdefiniuj publiczną strukturęMemoryz publicznym polemsize_gb(u32) określającym rozmiar pamięci. Dodaj metodęspecs, która zwraca String w formacie{size_gb}GB RAM.main.rs: Dołącz oba moduły i utwórz publiczną strukturęComputer, która ma polecputypuCPUoraz polememorytypuMemory. Zaimplementuj metodęsystem_infodlaComputer, która przekazuje zadanie swoim komponentom i wyświetla pełną specyfikację systemu. Użyj podanych danych wejściowych, aby utworzyćComputeri wyświetlić jego informacje.
Metoda system_info powinna wyświetlać:
System: {cpu_specs} | {memory_specs}Na przykład dla danych wejściowych 8, 3.5 i 16:
System: 8-core @ 3.5GHz | 16GB RAMA dla danych wejściowych 4, 2.8 i 32:
System: 4-core @ 2.8GHz | 32GB RAMOtrzymasz trzy dane wejściowe: liczbę rdzeni CPU (parsuj jako u32), częstotliwość CPU (parsuj jako f32) oraz rozmiar pamięci (parsuj jako u32).
Spróbuj swoich sił
mod cpu;
mod memory;
use cpu::CPU;
use memory::Memory;
// TODO: Zdefiniuj publiczną strukturę Computer z dwoma publicznymi polami:
// - cpu: CPU
// - memory: Memory
// TODO: Zaimplementuj metodę system_info w Computer, która:
// - Wywołuje cpu.specs() i memory.specs()
// - Wypisuje: System: {cpu_specs} | {memory_specs}
fn main() {
let mut input = String::new();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let cores: u32 = input.trim().parse().expect("Invalid number");
input.clear();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let speed_ghz: f32 = input.trim().parse().expect("Invalid number");
input.clear();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let size_gb: u32 = input.trim().parse().expect("Invalid number");
// TODO: Utwórz instancje CPU i Memory, używając wczytanych danych wejściowych
// TODO: Utwórz instancję Computer zawierającą CPU i Memory
// TODO: Wywołaj system_info() na instancji Computer, aby wypisać wynik
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Metody i zachowanie
Wprowadzenie do bloków implementacjiParametr selfMetody modyfikująceFunkcje powiązaneWiele bloków implementacjiŁańcuchowe wywoływanie metodPowtórzenie — działania na prostokącie4Projekt: Wirtualne zwierzątko
Definiowanie zwierzątkaKarmienie zwierzątka7Standardowe cechy
Cecha DebugCecha DisplayClone i CopyCechy równościPowtórka – punkt do wydrukowania10Projekt: System dokumentów
Trait DrawKomponent tekstowy2Hermetyzacja i moduły
Podstawy modułówSłowo kluczowe publicPola prywatneGetterySetteryPodsumowanie — bezpieczna skrytka5Typy generyczne
Struktury generyczneMetody generyczneWiele typów generycznychFunkcje generycznePodsumowanie – punkt współrzędnych8Cechy jako ograniczenia
Składnia ograniczeń cechWiele ograniczeńKlauzula whereZwracanie typów z użyciem cechPodsumowanie — generyczna drukarka11Wzorce projektowe w Rust
Wzorzec NewtypeKompozycjaTrait DropFrom i IntoPodsumowanie — atrapowy inteligentny wskaźnik3Zaawansowane typy wyliczeniowe
Typy wyliczeniowe z danymiMetody typów wyliczeniowychDopasowywanie wariantów danychTyp wyliczeniowy Option — powtórzeniePodsumowanie — typ wyliczeniowy Shape6Definiowanie cech
Czym jest cecha?Implementowanie cechImplementacje domyślneNadpisywanie wartości domyślnychCechy z parametramiPowtórzenie — odtwarzacz multimedialnyPoćwicz samodzielnie: Kompilator Rust online