Kompozisyon
Coddy'nin Rust Journey'sinin Object Oriented Programming bölümünün bir parçası — ders 55 / 61.
Pek çok nesne yönelimli dil, türler arasında işlevselliği paylaşmak için büyük ölçüde kalıtıma dayanır. Rust farklı bir yaklaşım benimser: bileşim. Bir ebeveynden davranış devralmak yerine, diğer türleri alanlar olarak dahil ederek karmaşık türler oluşturursunuz.
Bir arabayı modellediğinizi düşünün. Temel bir Vehicle sınıfı oluşturup ondan miras almak yerine, bir Car nesnesini daha küçük, odaklanmış bileşenlerden oluşturursunuz:
struct Engine {
horsepower: u32,
}
struct Wheels {
count: u8,
diameter: f32,
}
struct Car {
engine: Engine,
wheels: Wheels,
brand: String,
}
Car, Engine veya Wheels sınıflarından miras almaz; onlara sahiptir. Bu "has-a" (sahip olma) ilişkisi kompozisyondur (composition). İç bileşenlere alanları (fields) üzerinden erişirsiniz ve metotlarını doğrudan çağırabilirsiniz:
impl Engine {
fn start(&self) {
println!("Engine with {} HP started!", self.horsepower);
}
}
impl Car {
fn start(&self) {
self.engine.start(); // İç bileşene devret (Delegate)
}
}
Bu desen, her bir struct'ın tek bir sorumluluğa odaklanmasını sağlar. Motorların çalışma şeklini mi değiştirmeniz gerekiyor? Sadece Engine struct'ını değiştirin. Wheels'ı bir motosiklet için yeniden mi kullanmak istiyorsunuz? Onu yeni bir struct'a dahil etmeniz yeterli. Kompozisyon (Composition), kalıtımın (inheritance) genellikle oluşturduğu sıkı bağlılık (tight coupling) olmadan size esneklik sağlar.
Görev
KolayKompozisyon kullanarak bir bilgisayar sistemi inşa edelim! Tek bir devasa struct oluşturmak yerine, bir Computer yapısını daha küçük ve odaklanmış bileşenlerden —bir CPU ve Memory— oluşturacaksınız. Her bileşen kendini nasıl tanımlayacağını bilecek ve Computer bu görevi kendi parçalarına devredecek.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
cpu.rs: İki genel (public) alana sahip bir genelCPUstruct'ı tanımlayın: çekirdek sayısı içincores(u32) ve saat hızı içinspeed_ghz(f32). CPU'yu{cores}-core @ {speed_ghz}GHzformatında tanımlayan bir String döndürenspecsmetodunu ekleyin.memory.rs: Bellek boyutu için genel birsize_gb(u32) alanına sahip genel birMemorystruct'ı tanımlayın.{size_gb}GB RAMformatında bir String döndüren birspecsmetodu ekleyin.main.rs: Her iki modülü de dahil edin veCPUtipinde bircpualanına veMemorytipinde birmemoryalanına sahip olan genel birComputerstruct'ı oluşturun. Computer üzerinde, bileşenlerine delege eden ve tam sistem özelliklerini yazdıran birsystem_infometodu uygulayın. Bir Computer oluşturmak ve bilgilerini görüntülemek için sağlanan girdileri kullanın.
system_info metodu şunu yazdırmalıdır:
System: {cpu_specs} | {memory_specs}Örneğin, 8, 3.5 ve 16 girdileriyle:
System: 8-core @ 3.5GHz | 16GB RAMVe 4, 2.8 ve 32 girdileriyle:
System: 4-core @ 2.8GHz | 32GB RAMÜç girdi alacaksınız: CPU çekirdekleri (u32 olarak ayrıştırın), CPU hızı (f32 olarak ayrıştırın) ve bellek boyutu (u32 olarak ayrıştırın).
Kopya kağıdı
Rust, türler arasında işlevselliği paylaşmak için kalıtım yerine kompozisyon kullanır. Karmaşık türleri, diğer türleri alanlar (fields) olarak dahil ederek oluşturur ve bir "sahip olma" (has-a) ilişkisi kurarsınız.
Temel Kompozisyon
Daha küçük, odaklanmış struct'lar tanımlayın ve bunları daha büyük olanlarda birleştirin:
struct Engine {
horsepower: u32,
}
struct Wheels {
count: u8,
diameter: f32,
}
struct Car {
engine: Engine,
wheels: Wheels,
brand: String,
}
Bileşenlere Delege Etme
İç bileşenlere alanları üzerinden erişin ve metotlarını çağırın:
impl Engine {
fn start(&self) {
println!("Engine with {} HP started!", self.horsepower);
}
}
impl Car {
fn start(&self) {
self.engine.start(); // İç bileşene delege et
}
}
Kompozisyon, her struct'ın tek bir sorumluluğa odaklanmasını sağlayarak, sıkı bağlılık (tight coupling) olmadan esneklik sağlar.
Kendin dene
mod cpu;
mod memory;
use cpu::CPU;
use memory::Memory;
// TODO: İki genel (public) alana sahip genel bir Computer yapısı (struct) tanımlayın:
// - cpu: CPU
// - memory: Memory
// TODO: Computer üzerinde şunları yapan bir system_info metodu uygulayın:
// - cpu.specs() ve memory.specs() metodlarına yönlendirme yapan
// - Şunu yazdıran: 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: Ayrıştırılmış girdileri kullanarak CPU ve Memory örnekleri (instances) oluşturun
// TODO: CPU ve Memory ile bir Computer örneği (instance) oluşturun
// TODO: Sonucu yazdırmak için bilgisayar üzerinde system_info() metodunu çağırın
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Object Oriented Programming bölümündeki tüm dersler
1Metotlar ve Davranış
Uygulama Bloklarına GirişSelf ParametresiDeğiştirilebilir Metotlarİlişkili FonksiyonlarBirden Fazla Uygulama BloğuMetot ZincirlemeÖzet - Dikdörtgen Eylemleri4Proje: Sanal Evcil Hayvan
Evcil Hayvanı TanımlamaEvcil Hayvanı Besleme2Kapsülleme ve Modüller
Modül TemelleriPublic Anahtar KelimesiPrivate AlanlarGetter'larSetter'larÖzet - Güvenli Kasa5Generic Yapılar
Generic Struct'larGeneric MetotlarÇoklu Generic TürlerGeneric FonksiyonlarÖzet - Koordinat Noktası8Sınırlandırıcı Olarak Trait'ler
Trait Sınırlandırıcı SözdizimiÇoklu SınırlandırıcılarWhere CümleciğiTrait'ler ile Tip DöndürmeÖzet - Generic Yazıcı11Rust'ta Tasarım Kalıpları
Newtype KalıbıKompozisyonDrop Trait'iFrom ve IntoÖzet - Smart Pointer Mock3İleri Seviye Enum'lar
Veri İçeren Enum'larEnum MetotlarıVeri Varyantlarını EşlemeOption Enum'ına Yeniden BakışÖzet - Shape Enum6Trait Tanımlama
Trait Nedir?Traitleri UygulamaVarsayılan UygulamalarVarsayılanları Geçersiz KılmaParametreli TraitlerÖzet - Media Player