Bygge komplekse objekter med komposisjon.
Komposisjon og aggregering
Du har lært om arv, der en klasse er en spesialisering av en annen. Men ofte ønsker vi at en klasse har en annen som del av seg selv. Dette kalles komposisjon eller aggregering, og er et kraftig alternativ til arv.
I dette kapittelet skal du lære hvordan du bygger komplekse objekter ved å kombinere enklere objekter, og hvorfor "favor composition over inheritance" er et viktig designprinsipp.
Aggregering er en svakere form for komposisjon der inneholdte objekter kan eksistere uavhengig av container-objektet.
Delegering betyr å overlate ansvar til et innebygget objekt i stedet for å arve oppførsel.
Eksempel: Bil har en motor
I stedet for at Bil arver fra Motor, kan vi la Bil ha en 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 en Motor, og delegerer start/stopp-oppgaver til den.
"Foretrekk å bygge funksjonalitet ved å kombinere objekter (komposisjon) fremfor å arve oppførsel (arv)."
Fordeler med komposisjon:
- Mer fleksibel – kan endre oppførsel ved runtime
- Løsere kobling – objekter er mindre avhengige av hverandre
- Enklere testing – kan enkelt bytte ut deler
- Unngår problemer med dyp 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 (mer 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 bytte oppførsel uten å lage nye klasser!
Eksempel: Aggregering vs. komposisjon
Komposisjon (tight kobling): Hjerte eksisterer ikke uten 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 (løs kobling): 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 brukes begge termer ofte om hverandre.
Eksempel: Delegering i praksis
La oss bygge et 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()Spillet har grafikk, lyd og inndata-objekter, og delegerer ansvar til dem.
Oppsummering
I dette kapittelet har du lært:
- Komposisjon betyr at en klasse inneholder objekter av andre klasser ("har-en")
- Aggregering er en svakere form der inneholdte objekter kan eksistere uavhengig
- Delegering er å overlate ansvar til inneholdte objekter
- "Favor composition over inheritance" – foretrekk komposisjon for fleksibilitet
- Komposisjon gir løsere kobling og gjør kode mer vedlikeholdbar
Neste steg: Vi skal bruke komposisjon til å bygge datastrukturer!
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.