Öröklés és összetétel (Is-a vs Has-a kapcsolat) a Java-ban

1. Áttekintés

Az öröklés és a kompozíció - az absztrakcióval, a kapszulázással és a polimorfizmussal együtt - az objektum-orientált programozás (OOP) sarokköve.

Ebben az oktatóanyagban bemutatjuk az öröklés és az összetétel alapjait, és nagy hangsúlyt fektetünk a kétféle kapcsolat közötti különbségek észlelésére.

2. Az öröklés alapjai

Az öröklés egy erőteljes, mégis túlzott és visszaélt mechanizmus.

Egyszerűen fogalmazva, az örökléssel együtt egy alaposztály (más néven alaptípus) meghatározza az adott típusra jellemző állapotot és viselkedést, és lehetővé teszi, hogy az alosztályok (más néven altípusok) ennek az állapotnak és viselkedésnek speciális verzióit biztosítsák.

Ahhoz, hogy világos elképzelésünk legyen arról, hogyan lehet az örökléssel dolgozni, készítsünk egy naiv példát: egy alaposztályt Személy amely meghatározza az ember közös területeit és módszereit, míg az alosztályok Pincérnő és Színésznő további, finom szemcsés módszer-megvalósításokat biztosít.

Itt van a Személy osztály:

public class Személy {private final Sztring név; // egyéb mezők, standard kivitelezők, getterek}

És ezek az alosztályok:

public class Pincérnő meghosszabbítja a személyt {public String serveStarter (String starter) {return "Serving a" + starter; } // további módszerek / konstruktorok} 
public class Színésznő kiterjeszti a Person {public String readScript (String movie) {return "Read the script of +" film; } // további módszerek / konstruktorok}

Ezenkívül hozzunk létre egy egység tesztet annak ellenőrzésére, hogy a Pincérnő és Színésznő osztályok is példái Személy, ezzel megmutatva, hogy az „is-a” feltétel teljesül a típus szintjén:

@Test public void givenWaitressInstance_whenCheckedType_thenIsInstanceOfPerson () {assertThat (új Pincérnő ("Mary", "[e-mail védett]", 22)) .isInstanceOf (Person.class); } @Test public void givenActressInstance_whenCheckedType_thenIsInstanceOfPerson () {assertThat (új színésznő ("Susan", "[e-mail védett]", 30)) .isInstanceOf (Person.class); }

Fontos itt hangsúlyozni az öröklés szemantikai aspektusát. Eltekintve attól, hogy újra felhasználják a Személy osztály, hoztunk létre egy jól definiált „is-a” kapcsolatot az alaptípus között Személy és az altípusok Pincérnő és Színésznő. A pincérnők és színésznők tulajdonképpen személyek.

Ez arra késztetheti bennünket, hogy megkérdezzük: mely felhasználási esetekben az öröklés helyes megközelítés?

Ha az altípusok teljesítik az „is-a” feltételt, és főként additív funkciókat biztosítanak az osztályok hierarchiájában,akkor az öröklés az út.

Természetesen a módszer felülírása megengedett mindaddig, amíg az felülbírált módszerek megőrzik a Liskov Substitution Principle által támogatott alaptípus / altípus helyettesíthetőséget.

Emellett ezt is szem előtt kell tartanunk az altípusok öröklik az alaptípus API-ját, amely egyes esetekben túlzott vagy egyszerűen nemkívánatos lehet.

Ellenkező esetben inkább kompozíciót kell használnunk.

3. Öröklés a tervezési mintákban

Míg a konszenzus szerint az összetételnek előnyben kell részesítenünk az öröklést, amikor csak lehetséges, van néhány tipikus felhasználási eset, amikor az öröklésnek megvan a maga helye.

3.1. A réteg Supertype minta

Ebben az esetben mi örökléssel helyezze át a közös kódot egy alaposztályba (a szupertípusba), rétegenként.

Itt található ennek a mintának a megvalósítása a tartományrétegben:

public class Entity {védett hosszú id; // beállítók} 
public class Felhasználó kiterjeszti az entitást {// további mezők és módszerek} 

Ugyanezt a megközelítést alkalmazhatjuk a rendszer többi rétegeire is, például a szolgáltatási és a perzisztencia rétegekre.

3.2. A sablon módszer mintája

A sablon módszer mintájában megtehetjük használjon egy alaposztályt egy algoritmus invariáns részeinek meghatározásához, majd valósítsa meg a variáns részeket az alosztályokban:

public abstract class ComputerBuilder {public final Computer buildComputer () {addProcessor (); addMemory (); } public abstract void addProcessor (); public abstract void addMemory (); } 
public class StandardComputerBuilder kiterjeszti a ComputerBuilder {@Orride public void addProcessor () {// metódus implementáció} @Orride public void addMemory () {// metódus implementáció}}

4. A kompozíció alapjai

A kompozíció egy másik mechanizmus, amelyet az OOP biztosít a megvalósítás újrafelhasználására.

Dióhéjban, A kompozíció lehetővé teszi számunkra, hogy más objektumokból álló tárgyakat modellezzünk, így meghatározva a közöttük lévő „van-e” kapcsolatot.

Továbbá, a kompozíció a legerősebb társulási forma, ami azt jelenti az objektum (ok), amelyek egy objektumot alkotnak vagy tartalmaznak, szintén megsemmisülnek.

Tegyük fel, hogy a kompozíciók működésének jobb megértése érdekében a számítógépeket reprezentáló objektumokkal kell dolgoznunk.

A számítógép különféle részekből áll, beleértve a mikroprocesszort, a memóriát, a hangkártyát és így tovább, így a számítógépet és annak egyes részeit külön osztályként modellezhetjük.

Így lehet a Számítógép osztály nézhet ki:

nyilvános osztályú Computer {private Processor processzor; privát memória; privát SoundCard hangkártya; // standard getters / setters / konstruktorok public Opcionális getSoundCard () {return Optional.ofNullable (soundCard); }}

A következő osztályok mikroprocesszort, memóriát és hangkártyát modelleznek (az interfészek a rövidség kedvéért el vannak hagyva):

public class StandardProcessor implementálja a Processzor {private String modellt; // szokásos getters / setters}
public class StandardMemory megvalósítja a Memory {private String márkát; saját karakterlánc mérete; // szabványos kivitelezők, getterek, toString} 
nyilvános osztály A StandardSoundCard megvalósítja a SoundCard {private String márkát; // szabványos kivitelezők, getterek, toString} 

Könnyű megérteni az öröklésen felüli összetétel ösztönzésének motivációit. Minden olyan esetben, amikor szemantikailag helyes „van” kapcsolat alakul ki egy adott osztály és mások között, a kompozíció a megfelelő választás.

A fenti példában Számítógép teljesíti a „has-a” feltételt az osztályokat, amelyek az alkatrészeit modellezik.

Azt is érdemes megjegyezni, hogy ebben az esetben a tartalmazó Számítógép objektum tulajdonosa a benne lévő objektumoknak ha, és csak akkor ha az objektumokat nem lehet újra felhasználni egy másikban Számítógép tárgy. Ha tehetik, az összesítést használnánk az összetétel helyett, ahol a tulajdonjog nem feltételezhető.

5. Összetétel absztrakció nélkül

Alternatív megoldásként meghatározhatnánk a kompozíció kapcsolatát a Számítógép osztály, ahelyett, hogy deklarálná őket a konstruktorban:

public class Computer {privát StandardProcessor processzor = új StandardProcessor ("Intel I3"); privát StandardMemory memória = új StandardMemory ("Kingston", "1TB"); // további mezők / módszerek}

Természetesen ez egy merev, szorosan összekapcsolt kialakítás lenne, ahogy mi készítenénk Számítógép erősen függ a Processzor és memória.

Nem használnánk ki az interfészek és a függőség-injektálás által biztosított absztrakciós szintet.

Az interfészekre épülő kezdeti tervezéssel lazán összekapcsolt kialakítást kapunk, amelyet szintén könnyebb tesztelni.

6. Következtetés

Ebben a cikkben megismertük az öröklődés és az összetétel alapjait a Java-ban, és alaposan feltártuk a két típusú kapcsolat („is-a” és „has-a”) közötti különbségeket.

Mint mindig, az ebben az oktatóanyagban bemutatott összes kódminta elérhető a GitHubon.