Når man lærer objektorienteret programmering, og efter at have lært det grundlæggende om objekter, felter og metoder, bruger man det meste af tiden på nedarvning. Arv betyder, at vi overtager en del af implementeringen fra en basisklasse. Man skal bare oprette en underklasse af en basisklasse for at kunne arve alle ikke-private felter og metoder.
En bil og et fly er køretøjer, så det er indlysende, at begge disse klasser skal udvides fra deres fælles basisklasse kaldet Vehicle. Dette er et typisk akademisk eksempel, men når vi beslutter os for at binde disse klasser med arverelationen, skal vi være opmærksomme på nogle konsekvenser.
Fig. 1 Implementering af arverelation.
I dette tilfælde er klasserne tæt forbundet med hinanden - det betyder, at ændringer i hver klasses opførsel kan opnås ved at foretage ændringer i basisklassen. Kode. Det kan både være en fordel og en ulempe - det afhænger af, hvilken adfærd vi forventer. Hvis arven anvendes på det forkerte tidspunkt, kan processen med at tilføje en ny funktion støde på nogle implementeringsvanskeligheder, fordi den ikke passer til den oprettede klassemodel. Vi bliver nødt til at vælge mellem at duplikere koden og omorganisere vores model - og det kan være en meget tidskrævende proces. Vi kan kalde den kode, der udfører arverelationen, for "åben-lukket" - det betyder, at den er åben for udvidelser, men lukket for ændringer. Hvis vi antager, at der i Vehicle-klassen er en generel, defineret motordrift for hvert køretøj, vil vi være nødt til at foretage nogle alvorlige ændringer af vores klasser i det øjeblik, vi ønsker at tilføje en motorløs køretøjsmodel (f.eks. en cykel) til vores klassehierarki.
klasse Køretøj
def start_motor
slut
def stop_engine
end
end
klasse Fly < Køretøj
def move
start_motor
...
stop_engine
end
slut
Sammensætning
Hvis vi kun er interesseret i en del af den eksisterende klasses adfærd, er et godt alternativ til arv at bruge komposition. I stedet for at lave underklasser, der arver alle funktioner (dem, vi har brug for, og dem, vi slet ikke har brug for), kan vi isolere de funktioner, vi har brug for, og udstyre vores objekter med referencer til dem. På den måde opgiver vi tanken om, at objektet er en type af et basisobjekt, til fordel for påstanden om, at Den indeholder kun nogle dele af dens egenskaber.
Fig. 2 Brug af kompositionen
Med denne tilgang kan vi isolere den kode, der er ansvarlig for motordriften, til den selvstændige klasse kaldet Engine og kun henvise til den i de klasser, der repræsenterer køretøjer med motorer. Isolering af funktionerne ved hjælp af komposition vil gøre køretøjets klassestruktur enklere og styrke indkapslingen af de enkelte klasser. Nu er den eneste måde, køretøjerne kan påvirke motoren på, at bruge dens offentlige interface, fordi de ikke længere har oplysninger om dens implementering. Desuden giver det mulighed for at bruge forskellige typer motorer i forskellige køretøjer og endda udveksle dem, mens programmet kører. Det er selvfølgelig ikke fejlfrit at bruge komposition - vi skaber et løst forbundet klassesæt, som nemt kan udvides og er åbent for ændringer. Men samtidig er hver klasse forbundet med mange andre klasser og skal have oplysninger om deres grænseflader.
klasse Køretøj
ende
klasse Motor
def start
end
def stop
end
slut
klasse Fly < Køretøj
def initialize
@motor = Motor.ny
end
def move
@engine.start
@engine.stop
end
def change_engine(ny_motor)
@engine = ny_engine
end
end
Valget
Begge de beskrevne tilgange har fordele og ulemper, så hvordan vælger man mellem dem? Arv er en specialisering, så det er bedst kun at anvende dem til problemer, hvor der er "is-a"-typerelationer - så vi har at gøre med det virkelige hierarki af typer. Fordi nedarvning knytter klasser tæt sammen, bør vi for det første altid overveje, om vi skal bruge komposition eller ej. Komposition bør anvendes til problemer, hvor der er "har-en"-typerelationer - så klassen har mange dele, men den er noget mere end et sæt af klasser. Et fly består af dele, men alene er det noget mere - det har yderligere evner, f.eks. at flyve. Hvis man går videre med dette eksempel, kan de enkelte dele forekomme i forskellige specialiserede varianter, og så er det et godt tidspunkt at bruge nedarvning.
Både arv og komposition er kun værktøjer, som programmørerne har til rådighed, så det kræver erfaring at vælge det rigtige værktøj til et bestemt problem. Så lad os øve os og lære af vores fejl 🙂 .