Komposition, aggregering og tilknytning i Java

1. Introduktion

Objekter har forhold mellem dem, både i det virkelige liv og i programmeringen. Nogle gange er det svært at forstå eller implementere disse forhold.

I denne vejledning vil vi fokusere på Java's påtagelse af tre undertiden let blandede typer af relationer: komposition, sammenlægning og tilknytning.

2. Sammensætning

Sammensætning er en ”tilhører” type forhold. Det betyder, at et af objekterne er en logisk større struktur, der indeholder det andet objekt. Med andre ord er det en del eller medlem af det andet objekt.

Alternativt vi kalder det ofte et ”has-a” forhold (i modsætning til et "is-a" forhold, som er arv).

For eksempel hører et værelse til en bygning, eller med andre ord en bygning har et rum. Så grundlæggende er det kun et synspunkt om vi kalder det "tilhører" eller "har-a".

Komposition er en stærk slags "has-a" -forhold, fordi det indeholdende objekt ejer det. Derfor, genstandenes livscyklus er bundet. Det betyder, at hvis vi ødelægger ejerobjektet, vil dets medlemmer også blive ødelagt med det. For eksempel ødelægges rummet med bygningen i vores tidligere eksempel.

Bemærk, at det ikke betyder, at det indeholdende objekt ikke kan eksistere uden nogen af ​​dets dele. For eksempel kan vi nedbryde alle væggene inde i en bygning og dermed ødelægge værelserne. Men bygningen vil stadig eksistere.

Med hensyn til kardinalitet kan et indeholdende objekt have så mange dele, som vi ønsker. Imidlertid, alle delene skal have nøjagtigt en container.

2.1. UML

I UML angiver vi sammensætning med følgende symbol:

Bemærk, at diamanten er ved det indeholdende objekt og er bunden af ​​linjen, ikke en pilspids. Af hensyn til klarheden tegner vi ofte også pilespidsen:

Så så kan vi bruge denne UML-konstruktion til vores eksempel på bygningsrum:

2.2. Kildekode

I Java kan vi modellere dette med en ikke-statisk indre klasse:

klassebygning {klasselokale {}}

Alternativt kan vi også erklære denne klasse i en metodekropp. Det betyder ikke noget, om det er en navngivet klasse, en anonym klasse eller en lambda:

klasse Bygning {Værelse createAnonymousRoom () {returner nyt rum () {@ Override ugyldigt doInRoom () {}}; } Værelse createInlineRoom () {klasse InlineRoom implementerer rum {@Override ugyldigt doInRoom () {}} returner nyt InlineRoom (); } Værelse createLambdaRoom () {return () -> {}; } grænseflade Rum {ugyldigt doInRoom (); }}

Bemærk, at det er vigtigt, at vores indre klasse skal være ikke-statisk, da den binder alle dens forekomster til den indeholdende klasse.

Normalt vil det indeholdende objekt få adgang til dets medlemmer. Derfor skal vi gemme deres referencer:

klasse Bygning {Listelokaler; klasselokale {}}

Bemærk, at alle indre klasseobjekter gemmer en implicit henvisning til deres indeholdende objekt. Som et resultat behøver vi ikke gemme det manuelt for at få adgang til det:

klasse bygning {streng adresse; klasseværelse {String getBuildingAddress () {return Building.this.address; }}}

3. Aggregering

Aggregering er også et "has-a" forhold. Hvad adskiller det fra komposition, at det ikke indebærer at eje. Som et resultat er objekternes livscyklus ikke bundet: hver enkelt af dem kan eksistere uafhængigt af hinanden.

For eksempel en bil og dens hjul. Vi kan tage hjulene af, og de vil stadig eksistere. Vi kan montere andre (allerede eksisterende) hjul eller installere disse på en anden bil, og alt fungerer fint.

Naturligvis vil en bil uden hjul eller et løsrevet hjul ikke være så nyttig som en bil med hjulene på. Men det var derfor dette forhold eksisterede i første omgang: at samle delene til en større konstruktion, som er i stand til flere ting end dens dele.

Da aggregering ikke indebærer at eje, et medlem behøver ikke at være bundet til kun en container. For eksempel er en trekant lavet af segmenter. Men trekanter kan dele segmenter som deres sider.

3.1. UML

Aggregering minder meget om sammensætning. Den eneste logiske forskel er aggregering er et svagere forhold.

Derfor er UML-repræsentationer også meget ens. Den eneste forskel er, at diamanten er tom:

For biler og hjul ville vi så gøre:

3.2. Kildekode

I Java kan vi modellere aggregering med en almindelig gammel reference:

klassehjul {} klasse bil {listehjul; }

Medlemmet kan være en hvilken som helst type klasse undtagen en ikke-statisk indre klasse.

I kodestykket ovenfor har begge klasser deres separate kildefil. Vi kan dog også bruge en statisk indre klasse:

klasse bil {listehjul; statisk klassehjul {}}

Bemærk, at Java kun opretter en implicit reference i ikke-statiske indre klasser. Derfor er vi nødt til at opretholde forholdet manuelt, hvor vi har brug for det:

klasse Hjul {bil bil; } klasse bil {listehjul; }

4. Forening

Forening er det svageste forhold mellem de tre. Det er ikke et ”has-a” forhold, ingen af ​​objekterne er dele eller medlemmer af en anden.

Forening betyder kun, at objekterne "kender" hinanden. For eksempel en mor og hendes barn.

4.1. UML

I UML kan vi markere en tilknytning med en pil:

Hvis foreningen er tovejs, kan vi bruge to pile, en pil med en pilespids i begge ender eller en linje uden pilehoveder:

Vi kan repræsentere en mor og hendes barn i UML og derefter:

4.2. Kildekode

I Java kan vi modellere tilknytning på samme måde som aggregering:

klasse Barn {} klasse Mor {Liste børn; }

Men vent, hvordan kan vi fortælle, om en reference betyder sammenlægning eller tilknytning?

Det kan vi ikke. Forskellen er kun logisk: om et af objekterne er en del af det andet eller ej.

Vi er også nødt til at vedligeholde referencerne manuelt i begge ender, som vi gjorde med aggregering:

klasse Barn {Moder mor; } klassemoder {liste børn; }

5. UML Sidenote

Af hensyn til klarheden ønsker vi nogle gange at definere kardinaliteten i et forhold på et UML-diagram. Vi kan gøre dette ved at skrive det til enderne af pilen:

Bemærk, at det ikke giver mening at skrive nul som kardinalitet, fordi det betyder, at der ikke er noget forhold. Den eneste undtagelse er, når vi vil bruge et interval til at angive et valgfrit forhold:

Bemærk også, at da der i sammensætningen er nøjagtigt en ejer, angiver vi det ikke på diagrammerne.

6. Et komplekst eksempel

Lad os se et (lidt) mere komplekst eksempel!

Vi modellerer et universitet, der har sine afdelinger. Professorer arbejder i hver afdeling, som også har venner indbyrdes.

Vil afdelingerne eksistere, når vi lukker universitetet? Selvfølgelig ikke, derfor er det en sammensætning.

Men professorerne vil stadig eksistere (forhåbentlig). Vi er nødt til at beslutte, hvad der er mere logisk: hvis vi betragter professorer som dele af afdelingerne eller ej. Alternativt: er de medlemmer af afdelingerne eller ej? Ja, det er de. Derfor er det en sammenlægning. Derudover kan en professor arbejde i flere afdelinger.

Forholdet mellem professorer er tilknytning, fordi det ikke giver mening at sige, at en professor er en del af en anden.

Som et resultat kan vi modellere dette eksempel med følgende UML-diagram:

Og Java-koden ser sådan ud:

klasse Universitet {Listafdeling; } klasse Afdeling {Listeprofessorer; } klasseprofessor {Listeafdeling; Liste venner; }

Bemærk, at hvis vi stole på udtrykkene "har-a", "tilhører", "medlem-af", "del af"og så videre kan vi lettere identificere forholdet mellem vores objekter.

7. Konklusion

I denne artikel så vi egenskaberne og repræsentationen af ​​sammensætning, sammenlægning og tilknytning. Vi så også, hvordan man modellerer disse forhold i UML og Java.

Som sædvanligt er eksemplerne tilgængelige på GitHub.