Bygge komplekse objekter med komposisjon.
Komposisjon og aggregering
Du har lært om arv, der ein klasse er ein spesialisering av ein annan. Men ofte ønskjer vi at ein klasse har ein annan som del av seg sjølv. Dette vert kalla komposisjon eller aggregering, og er eit kraftig alternativ til arv.
I dette kapittelet skal du lære korleis du byggjer komplekse objekt ved å kombinere enklare objekt, og kvifor "favor composition over inheritance" er eit viktig designprinsipp.
Aggregering er ei svakare form for komposisjon der inneheldne objekt kan eksistere uavhengig av container-objektet.
Delegering tyder å overlate ansvar til eit innebygd objekt i staden for å arve oppførsel.
Eksempel: Bil har ein motor
I staden for at Bil arvar frå Motor, kan vi la Bil ha ein Motor:
class Motor:
def __init__(self, hestekrefter):
self.hestekrefter = hestekrefter
self.kjorer = False
def start(self):
self.kjorer = True
print(f"Motor med {self.hestekrefter} hk startet!")
def stopp(self):
self.kjorer = False
print("Motor stoppet.")
class Bil:
def __init__(self, merke, hestekrefter):
self.merke = merke
self.motor = Motor(hestekrefter) # Komposisjon!
def start_bil(self):
print(f"{self.merke} starter...")
self.motor.start() # Delegering
def stopp_bil(self):
self.motor.stopp()
print(f"{self.merke} stoppet.")
# Bruk
min_bil = Bil("Tesla Model 3", 450)
min_bil.start_bil()
# Output:
# Tesla Model 3 starter...
# Motor med 450 hk startet!Her har Bil ein Motor, og delegerer start/stopp-oppgåver til han.
"Føretrekk å byggje funksjonalitet ved å kombinere objekt (komposisjon) framfor å arve oppførsel (arv)."
Fordelar med komposisjon:
- Meir fleksibel – kan endre oppførsel ved runtime
- Lausare kopling – objekt er mindre avhengige av kvarandre
- Enklare testing – kan enkelt byte ut delar
- Unngår problem med djupt arvshierarki
Eksempel: Komposisjon vs. arv
Med arv (mindre fleksibelt):
class Flygende:
def fly(self):
print("Flyr!")
class Svømmende:
def svøm(self):
print("Svømmer!")
class And(Flygende, Svømmende): # Multippel arv
pass
# Hva om vi vil ha en and som ikke kan fly?
# Må lage ny klasse...Med komposisjon (meir fleksibelt):
class FlyOppførsel:
def utfør(self):
print("Flyr!")
class SvømOppførsel:
def utfør(self):
print("Svømmer!")
class IngenFlyOppførsel:
def utfør(self):
print("Kan ikke fly.")
class And:
def __init__(self, fly_oppførsel, svøm_oppførsel):
self.fly_oppførsel = fly_oppførsel
self.svøm_oppførsel = svøm_oppførsel
def fly(self):
self.fly_oppførsel.utfør()
def svøm(self):
self.svøm_oppførsel.utfør()
# Nå kan vi enkelt lage forskjellige ender:
and1 = And(FlyOppførsel(), SvømOppførsel())
and1.fly() # Flyr!
and2 = And(IngenFlyOppførsel(), SvømOppførsel())
and2.fly() # Kan ikke fly.Med komposisjon kan vi byte oppførsel utan å lage nye klassar!
Eksempel: Aggregering vs. komposisjon
Komposisjon (tight kopling): Hjarte eksisterer ikkje utan person.
class Hjerte:
def __init__(self, bpm):
self.hjerteslag_per_minutt = bpm
def slå(self):
print(f"Hjerte slår {self.hjerteslag_per_minutt} ganger/min")
class Person:
def __init__(self, navn):
self.navn = navn
self.hjerte = Hjerte(70) # Lager hjertet her
# Når person slettes, slettes også hjertetAggregering (laus kopling): Bil kan ha hjul, men hjul kan eksistere separat.
class Hjul:
def __init__(self, størrelse):
self.størrelse = størrelse
class Bil:
def __init__(self, hjul_liste):
self.hjul = hjul_liste # Får hjul utenfra
# Hjul kan eksistere uavhengig av bil
hjul1 = Hjul(18)
hjul2 = Hjul(18)
hjul3 = Hjul(18)
hjul4 = Hjul(18)
min_bil = Bil([hjul1, hjul2, hjul3, hjul4])
# Hjulene eksisterer fortsatt selv om bil slettesI praksis vert begge termane ofte brukte om kvarandre.
Eksempel: Delegering i praksis
Lat oss byggje eit enkelt Spill-system med komposisjon:
class Grafikk:
def tegn_skjerm(self, tekst):
print(f"🎮 {tekst}")
class Lyd:
def spill_effekt(self, lyd):
print(f"🔊 {lyd}")
class Inndata:
def les_tast(self):
return input("Trykk tast > ")
class Spill:
def __init__(self):
self.grafikk = Grafikk()
self.lyd = Lyd()
self.inndata = Inndata()
self.poeng = 0
def start(self):
self.grafikk.tegn_skjerm("Spillet starter!")
self.lyd.spill_effekt("Start-musikk")
while True:
kommando = self.inndata.les_tast()
if kommando == "a":
self.angrip()
elif kommando == "q":
self.avslutt()
break
def angrip(self):
self.grafikk.tegn_skjerm("Du angriper!")
self.lyd.spill_effekt("Sverd-lyd")
self.poeng += 10
print(f"Poeng: {self.poeng}")
def avslutt(self):
self.grafikk.tegn_skjerm("Spillet avsluttes...")
self.lyd.spill_effekt("Avslutt-musikk")
# Bruk
# spill = Spill()
# spill.start()Spelet har grafikk-, lyd- og inndata-objekt, og delegerer ansvar til dei.
Oppsummering
I dette kapittelet har du lært:
- Komposisjon tyder at ein klasse inneheld objekt av andre klassar ("har-en")
- Aggregering er ei svakare form der inneheldne objekt kan eksistere uavhengig
- Delegering er å overlate ansvar til inneheldne objekt
- "Favor composition over inheritance" – føretrekk komposisjon for fleksibilitet
- Komposisjon gir lausare kopling og gjer kode meir vedlikehaldbar
Neste steg: Vi skal bruke komposisjon til å byggje datastrukturar!
Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.