Kihagyás

4. gyakorlat

A bemutatott példákhoz szükség van az előző órán elkészített Macska és Ember osztályokra.

A következőekben megnézzük az osztályok közötti lehetséges kapcsolatokat. Szeretnénk, ha az Ember és a Macska tudjanak egymásról, egy programbeli ember megsimogathasson egy programbeli macskát.

Az Ember osztály kódja valami ilyesmi:

public class Ember {
    private String nev;

    private int karmolasokSzama;

    private String email;

    public static int GMAIL_FIOKOK_DARAB = 0;

    public Ember(String nev, String email) {
        this.nev = nev;
        this.email = email;
        this.karmolasokSzama = 0;
    }

    public void karmolasNovel() {
        this.karmolasokSzama++;
    }

    public int getKarmolasokSzama() {
        return karmolasokSzama;
    }

    public String getNev() {
        return nev;
    }

}

Kapcsolatok osztályok között

Objektum-kölcsönhatásokat valósítanak meg. Alapvető kapcsolatok osztályok között: asszociáció, aggregáció (gyenge, erős), öröklődés. Ezen kapcsolatoknak különböző tulajdonságai lehetnek (név, irány, szerep, multiplicitás, stb).

Asszociáció

Osztályok közötti kétirányú összeköttetés, megadható az iránya (az üzenet iránya). A kapcsolatban résztvevő osztályok léte egymástól általában független, de legalább az egyik ismeri és/vagy használja a másikat. Gyakorlatilag az osztályokból létrejövő objektumok között van összefüggés.

Jele: a két osztály között húzott vonal, adott esetben nyíllal valamelyik végén. A kapcsolathoz tartozhat irány is, ezt sima nyíllal jelöljük.

Például, a Macska és az Ember osztály között lehet egy asszociáció, mivel az ember megsimogathatja a macskát.

Asszociáció Macska és Ember között

Az asszociációk a forráskódban számos módon előkerülhetnek, például: lokális változóként, függvény paraméterként, visszaadott értékként, stb. Ilyenkor legalább az egyik osztály ismeri a másikat, de persze lehet két irányú is az asszociáció, ha például a két példány között kölcsönös kapcsolat van.

Java kód

A példánkban az Ember megsimogathatja a Macska példányokat, a Macska simogatastKap metódusa által.

    public void simogatastKap(Ember ki) {
        simogatasokSzama++;
        if (!baratsagos) {
            ki.karmolasNovel();
            System.out.println(this.nev + " megkarmolta " + ki.getNev() + "-t!");

            if (simogatasokSzama > 30) {
                baratsagos = true;
            }
        }
    }

A metódus hatására növelődik az aktuális macska simogatasokSzama tulajdonsága, valamint ha nem barátságos, megkarmolja az embert, aki megsimogatja. A nem barátságos macskák idővel barátságossá válnak, ami után nem karmolnak.

Asszociációs osztály

Az asszociáció tulajdonságát az asszociációhoz húzott szaggatott vonallal jelölhetjük, ahogy azt a lenti példán is láthatjuk. A példán Macska és Orvos osztály közötti kapcsolatnak van egy Vizsgálat tulajdonsága, mivel az adott macska és adott orvos közötti kapcsolat számos tulajdonsággal rendelkezik.

Asszociáció

Egy másik példa

Asszociáció

Ebben a példában a Személy és a Lakás között van egy Lakik tulajdonság. Egy Személy pontosan 1 vagy 2 lakásban lakhat, de egy Lakás példányhoz bármennyi személy tartozhat.

Aggregáció

Az aggregáció egy rész-egész kapcsolatot jelent, az asszociáció egy speciális, erősebb formája. Az egyik objektum fizikailag is tartalmazza, vagy birtokolja a másikat. Az aggregáció jelölése a két osztály között húzott vonal, egy rombusszal a tartalmazó oldalán, ennek két változata lehet: üres rombusz vagy pedig teli rombusz.

Gyenge aggregáció

Egy speciális asszociáció, amelynél a tartalmazó létezhet a tartalmazott nélkül is, pl.: alma és kukac, vagy pedig postaláda és ház, ahol a ház és a postaláda is létezhet külön-külön, azonban minden házhoz tartozhat postaláda.

Jele: a két osztály között húzott vonal, egy üres rombusszal a tartalmazó oldalán

Példa gyenge aggregációra

A macskáknak lehet nyakörvük, amin rajta lehetnek a gazda adatai. A Macska példányhoz tartozhat egy Nyakörv példány, azonban ez a tartalmazás nem túlzottan szigorú, Macska létezhet Nyakörv nélkül is (és egyébként Nyakörv is Macska nélkül). Ha szeretnénk tartalmazást a kódunkban, készítsük el a Nyakörv osztályt is. Természetesen ez egy teljesen különálló osztály, néhány egyszerű adattaggal, getterekkel és szetterekkel. Az egyszerűség kedvéért csak a Macska neve lesz rajta, valamint a gazda neve, esetleg a nyakörveknek lehetnek bolhairtó tulajdonsága, ami esetén a kóborló macska példány nem szed össze annyi bolhát.

Az osztálydiagramon tartalmazást kétféleképpen reprezentálhatunk: simán csak adattagként egy másik osztály példánya, vagy pedig az aggregációra írjuk rá az adattag nevét, láthatóságát, egyéb adatait.

Példa gyenge aggregációra

Itt látható a számosság, hiszen egy Macska objektumhoz vagy 0 vagy 1 Nyakörv tartozhat. Készítsük el az implementációt.

Java kód

A Nyakörv osztály megvalósításának részleteit itt nem mutatjuk be külön, de valahogy így néz ki a kész osztály:

public class Nyakorv {
    private String nevCimke;
    private String gazdaNeve;

    private boolean bolhairto;

    public Nyakorv(String nevCimke, String gazdaNeve, boolean bolhairto) {
        this.nevCimke = nevCimke;
        this.gazdaNeve = gazdaNeve;
        this.bolhairto = bolhairto;
    }

    public Nyakorv(String nevCimke, String gazdaNeve) {
        this.nevCimke = nevCimke;
        this.gazdaNeve = gazdaNeve;
        this.bolhairto = true;
    }

    public String getNevCimke() {
        return nevCimke;
    }

    public void setNevCimke(String nevCimke) {
        this.nevCimke = nevCimke;
    }

    public String getGazdaNeve() {
        return gazdaNeve;
    }

    public void setGazdaNeve(String gazdaNeve) {
        this.gazdaNeve = gazdaNeve;
    }

    public boolean isBolhairto() {
        return bolhairto;
    }

    public void setBolhairto(boolean bolhairto) {
        this.bolhairto = bolhairto;
    }
}

A tartalmazás része a legegyszerűbb: csak felvesszük az osztályban az új adattagot.

public class Macska {
    private String nev;
    private double suly;
    private boolean baratsagos;
    private int bolhakSzama;
    private Nyakorv nyakorv;
    private int simogatasokSzama = 0;
    private static int BARATSAGTALAN_DARAB = 0;


    // ...
}

Valahol létre is kell tudni hozni, vagy beállítani Nyakörv példányt a Macska osztályon belül, például egy hagyományos setter segítségével, vagy pedig mondjuk a konstruktorban.

    public Macska(String nev, boolean baratsagos, String gazdaNeve) {
        this(nev, baratsagos);
        this.nyakorv = new Nyakorv(nev, gazdaNeve);
    }

Láthatjuk, hogy meghívjuk az osztály egy másik konstruktorát, majd pedig létrehozzuk a Nyakörv példányt. Ha szeretnénk valahol egyéb helyen is használni, létrehozhatunk például egy függvényt kobor néven, ami visszaadja, hogy az adott Macska példányunk kóbor-e. Egy macska akkor kóbor, ha nincs nyakörve (legalább is ebben a programban). Vegyük fel a diagramunkra a függvényt.

Macska osztály

A megvalósítás nem túl bonyolult. Tudjuk, hogy a Java referencia alapú nyelv, a nem primitív adattagok kezdőértéke null. Ha nem hoztunk létre nyakörvet, akkor a nyakorv adattag értéke null lesz. Ezt simán egyenlőség jellel hasonlítjuk össze, nem pedig az equals segítségével, mivel ha a nyakorv adattag értéke null (tehát nem létezik Nyakörv objektum, amire mutatna a nyakorv adattag), annak az equals metódusa sem létezik, így NullPointerException hibát kapnánk.

    public boolean kobor() {
        return nyakorv == null;
    }

Ha további működést szeretnénk készíteni, megoldhatjuk például, hogy a Macska koborol függvénye csak akkor adjon a macskánkhoz bolhát, ha nem bolhairtó nyakörvet hord.

    public void koborol(double tav) {
        suly--;
        if (nyakorv == null || !nyakorv.isBolhairto()) {
            bolhakSzama += (tav / 20) + 1;
        }
    }

Természetesen itt is előfordulhat, hogy nem létezik nyakorv adattag, így ezt előbb meg kell vizsgálnunk, mielőtt meghívnánk a macskán lévő nyakörv isBolhairto metódusát.

Egy másik egyszerű példa lehet a hűtő-ételek-kompresszor esete. Egy hűtőben tárolhatunk élelmiszereket, berakhatunk, kivehetünk belőle, sőt, lehet, hogy egy hűtőben egy élelmiszer sincs.

Hűtős példa

Kompozíció

Ez egy erősebb fizikai tartalmazás, itt a részek élettartama szigorúan megegyezik az egészével, pl.: macska-fej, lakás-gerendaszerkezet.

Jele: a két osztály között húzott vonal, egy teli rombusszal a tartalmazó oldalán.

Példa kompozícióra

A példában az Állatkórház és a Vizsgáló között erős tartalmazás, kompozíció van, hiszen a Vizsgálónak önmagában nincs értelme, csak az állatkórházon belül létezik. Azonban a Vizsgáló és a Macska között aggregáció van, mivel a Vizsgáló tartalmazhat Macskát, de a létük nem szigorúan függ egymástól.

Technikailag Java nyelven a szigorú tartalmazás nem teljesen kivitelezhető, a memória kezelése teljesen automatikus, így nincs lehetőségünk a kompozíciót betartatni (ellentétben például a C/C++-ra, ahol tárolhatunk érték/pointer/referencia szerint is). Azonban tervezéskor mégis különbséget kell tenni az aggregáció és a kompozíció között. Az előző nyakörves példában mivel Nyakörv példányt mindig csak a Macska konstruktorában hoztunk létre, akár kompozíciót is használhatnánk a diagramon.

Ha tervezünk, akkor alapvetően mindig a tartalmazás "erőssége" alapján válasszunk asszociáció/aggregáció/kompozíció közül. De talán ez az egyszerűbb elgondolás is segíthet:

  • "Has-A": Aggregation, aggregáció (tartalmaz, például a hűtő tartalmaz ételeket)
  • "Part-of": Composition, kompozíció (része, például a hűtő része a kompresszor)

A hűtőnek része a kompresszor, ezt a hűtő erősen tartalmazza. A kompozíció esetén általában a tartalmazott nem létezik a tartalmazója nélkül, külön Kompresszor példányokat nem igazán tudunk értelmezni, felhasználni a programunkban.

Hűtős példa

Java kód

Megvalósítása megegyezik a hagyományos aggregációval Java nyelven, általában adattag formájában láthatjuk a kompozíciókat.

Öröklődés

Egy olyan osztályok közötti kapcsolat, ahol az egyik osztály megosztja a struktúráját és/vagy viselkedését egy vagy több másik osztállyal. Ez jó dolog, mert nem kell újra megírnunk őket, viszont a szülőtől örökölt egyes metódusokat speciálisabban is megvalósíthatjuk, felülírhatjuk őket (override), vagy akár újakat is definiálhatunk. Öröklődéssel egy egész hierarchiát készíthetünk, a származtatott osztály örököl az ősosztály(ok)tól. Ez az újrafelhasználhatóság egyik alapeszköze. Az öröklődés relációnak nincs neve, multiplicitása. Öröklődés során az attribútumokat (egy programban gyakorlatilag adattagokat) és az operációkat (metódusokat) a lehető legfelső szinten definiálni kell. A származtatott (gyerek) osztály mindent örököl az őstől (attribútumokat, viselkedést, kapcsolatokat is), és kiegészítheti ezeket sajátokkal. A származtatott osztály egy örökölt operációnak saját implementációt is adhat. Ezt nevezzük felüldefiniálásnak (overriding), ez a polimorfizmus alapja.

A tipikus öröklődési szintek száma (ős-gyerek viszony) 3-5 körül szokott lenni.

Példa öröklődésre

Többszörös öröklődés

Előfordulhat, hogy egy osztály nem csak egy, hanem több ősosztálytól is örököl, azaz az összes ősének az összes tulajdonságát és operációját megörökli, ezt nevezzük többszörös öröklődésnek. Ezzel azonban érdemes vigyázni, hiszen ha egy osztály sok másik osztályból öröklődik közvetlenül, akkor az osztály könnyen átláthatatlanná, kezelhetetlenné válhat.

Példa többszörös öröklődésre

Megjegyzés

A legtöbb objektum-orientált nyelvben, így Java-ban, és C#-ban sincs többszörös öröklődés, de pl. a C++, Python nyelvekben van.

Java kód

Javaban csak egyszeres öröklődés van!

Ez azt jelenti, hogy egy osztálynak nem lehet kettő, vagy több közvetlen szülőosztálya. Azt viszont nem zárja ki, hogy egy osztálynak több gyerekosztálya legyen, vagy hogy a gyerekosztálynak lehessenek saját gyerekosztályai.

Találkozhatunk egy új láthatósággal, amelynek neve protected. Ennek a láthatóságnak a segítségével biztosíthatjuk, hogy az egyes adattagok, metódusok a gyermekosztályok számára is láthatóak legyenek, azonban a külvilág számára ne. Ezt a láthatóságot az UML diagramokban # jelöléssel láthatjuk, tehát az olyan tulajdonságok/operációk, amelyek # prefixszel kezdődnek, azokat a tervező protected láthatóságúvá tervezte.

A megkezdett macskás példánkat fogjuk folytatni. Azonban az eddigi Macska osztályt fogjuk folytatni, a Macska mellett lesz Tigris osztályunk is. A Tigris osztály mindent örököl a Macska osztálytól, minden tulajdonságot, viselkedést. Azonban mielőtt ezt megtennénk, a Macska osztályt is le kell tisztáznunk, szeretnénk kettéválasztani a két fogalmat, mivel az eddigi Macska osztályunk igaziból félig-meddig minden Macskaféléhez tartozó tulajdonságokat is, valamint házi macska tulajdonságokat is tartalmazott, így kezdjük el kettéválasztani ezt a két fogalmat.

Macska öröklődés

A példában láthatjuk, hogy a Macskaféle osztályban a bolhakSzama tulajdonság most már nem private, hanem protected láthatóságú, ami azt fogja jelenteni, hogy a leszármazott osztályokban kényelmesen elérhetjük. Viszont igaz, hogy ami private volt, azt a leszármazott osztályokból sem tudjuk direkt elérni, csak getter/szetter segítségével. Látható, hogy az egyik konstruktorunk is protected lett, valamint, hogy felvettünk egy új adattagot, ami az adott fajta Macskaféle pontos rendszertani nevét fogja tárolni. Ez a kódban az alábbi módon valósítható meg:

public class Macskafele {
    private String nev;
    private double suly;
    protected int bolhakSzama;
    private String rendszertaniBesorolas;

    public Macskafele(String nev, double suly) {
        this.nev = nev;
        this.suly = suly;
        this.bolhakSzama = 0;
        this.rendszertaniBesorolas = "Felidae";
    }

    protected Macskafele(String nev, double suly, String rendszertaniBesorolas) {
        this.nev = nev;
        this.suly = suly;
        this.bolhakSzama = 0;
        this.rendszertaniBesorolas = rendszertaniBesorolas;
    }

    public void nyavog() {
        System.out.println("<Nem tudni a pontos nyavogast.>");
    }

    public void koborol(double tav) {
        suly--;
        bolhakSzama += (tav / 20) + 1;
    }

    public void bolhatlanit() {
        this.bolhakSzama = 0;
    }

    // Generált getter és szetter függvények
}

Alapvetően minden Macskaféle a "Felidae" családba tartozik, ha ezt a tulajdonságát nem adjuk meg, azonban ezt a tulajdonságot csak a leszármazott osztályokból elérhető konstruktorral tudjuk beállítani.

Extends, super

Ez egy általános Macskaféle osztály, azonban lesznek speciálisabb osztályok a kódban, amik esetében szeretnénk használni a már elkészült Macskafele osztályt, de szeretnénk speciálisabb működést adni neki, esetleg kiegészíteni több tulajdonsággal. Mivel a "Macskaféle", mint fogalom nem igazán értelmezhető, hiszen a programban majd Házi macskákat, Tigriseket szeretnénk tárolni. Ehhez használhatjuk az öröklődést.

extends - ezzel a kulcsszóval érhetjük el az öröklődést, az osztály deklarációjában, az osztály neve után kell írnunk, majd az extends kulcsszó után az ősosztály nevét írjuk.

super - a gyerekosztályból hivatkozhatunk a szülőre, annak adattagjaira (amiket látunk) és metódusaira is, ezeket super.szuloMetodusanakNeve()-szerű parancsokkal érhetjük el.

public class HaziMacska extends Macskafele {
    private boolean baratsagos;
    private Nyakorv nyakorv;
    private int simogatasokSzama = 0;

    public HaziMacska(String nev, double suly, boolean baratsagos) {
        super(nev, suly, "Felis silvestris catus");

    }

    public void nyavog() {
        String meow = "Me";
        for (int i = 0; i < getSuly(); i++) {
            if (i % 2 == 0) {
                meow += "o";
            } else {
                meow += "O";
            }
        }
        meow += "w";
        System.out.println(getNev() + " nyávog: " + meow);
    }

    public void koborol(double tav) {
        setSuly(getSuly() - 1);
        if (nyakorv == null || !nyakorv.isBolhairto()) {
            bolhakSzama += (tav / 20) + 1;
        }
    }

    public boolean kobor() {
        return nyakorv == null;
    }

    public void simogatastKap(Ember ki) {
        simogatasokSzama++;
        if (!baratsagos) {
            ki.karmolasNovel();
            System.out.println(this.getNev() + " megkarmolta " + ki.getNev() + "-t!");

            if (simogatasokSzama > 30) {
                baratsagos = true;
            }
        }

    }
}

A szülő konstruktora pedig egyszerűen a super kulcsszó metódusként való használatával érhető el, például:

super(nev, suly, "Felis silvestris catus");

Ezzel inicializáljuk az ősosztály tulajdonságait, a Macskafele osztály String, double, boolean fejlécű konstruktorát hívjuk meg, aminek az ősosztályban protected láthatósága van.

Ha az ősosztály paraméter nélküli konstruktorát szeretnénk meghívni, akkor a super(); hívás a gyermekosztály konstruktorában elhagyható. Ha nem a default konstruktorát használjuk az ősosztálynak (vagy nincs default konstruktora), akkor viszont kötelező a super(arg1,arg2...argn); meghívása a gyerekosztály konstruktorában!

A gyerekosztályban láthatjuk, hogy az ősből örökölt nyavog() metódust felüldefiniáltuk (override), annak egy speciálisabb működést adtunk. A korábbi Macska osztályhoz képest van egy apró különbség: mivel a suly és a nev adattag privát láthatóságú, így ezekben az esetekben a getter/szetter függvényeken keresztül tudjuk elérni ezeket az adattagokat.

Természetesen további osztályokat is létrehozhatunk, például a tigriseknek, amelyek speciális macskafélék, majd pedig egy speciális tigrisfajtának, a kardfogú tigriseknek.

public class Tigris extends Macskafele {
    public Tigris(String nev, double suly) {
        super(nev, suly, "Panthera tigris");
    }

    @Override
    public void nyavog() {
        String meow = "Ra";
        for (int i = 0; i < getSuly(); i++) {
            if (i % 2 == 0) {
                meow += "a";
            } else {
                meow += "A";
            }
        }
        meow += "wR";
        System.out.println(getNev() + " üvölt: " + meow);
    }

    public void erosebb(Tigris masik) {
        if (this.getSuly() > masik.getSuly()) {
            this.nyavog();
            System.out.println("A üvöltésemmel elzavartam a másik tigrist!");
        } else {
            masik.nyavog();
            System.out.println("A másik tigris üvöltésétől megijedtem, el kell menekülnöm...");
        }
    }

}

A Tigris osztályban szintén felüldefiniáljuk az örökölt nyavog metódust. A Tigris osztálynak a Macskafele osztályhoz képest nincs semmilyen speciális plusz tulajdonsága, természetesen megörököl mindent a Macskafele osztálytól. Azonban van egy saját metódusa, ami speciális a Tigris példányokra, ez pedig az erosebb metódus, ami az adott tigrisről el tudja dönteni, hogy erősebb-e, mint a paraméterben érkező tigris. Egy tigris akkor erősebb egy másiknál, ha nagyobb a súlya, tehát nagyobbat üvölt (nyávog).

A KardfoguTigris osztály reprezentál egy speciális Tigris fajtát (egyébként egyúttal speciális Macskafele fajtát is). Az örökölt nyavog metódust itt is felüldefiniáljuk, mivel a kardfogú tigrisek még nagyobbat tudnak üvölteni, mint egy sima tigris.

public class KardfoguTigris extends Tigris {
    private int szemfogMeret;
    public KardfoguTigris(String nev, double suly) {
        super(nev, suly);
    }

    @Override
    public void nyavog() {
        String meow = "Ra";
        for (int i = 0; i < getSuly(); i++) {
            if (i % 2 == 0) {
                meow += "aa";
            } else {
                meow += "AA";
            }
        }
        meow += "wR";
        System.out.println(getNev() + " üvölt: " + meow);
    }
}

Ahogy látható, minden Tigris (és KardfoguTigris) egyben Macskaféle is, viszont nem minden Macskaféle lesz Tigris (vagy KardfoguTigris). A HáziMacska osztály speciális esete a Macskaféle osztálynak, hiszen a házi macskák speciális macskafelék. Ez a másik "irányból" nézve: a Macska osztály általánosítása a Macskaféle osztálynak, hiszen a macskafélék a speciális macskák (Tigris, Házi macska, stb.) általános, közös tulajdonságait, viselkedését tartalmazó őse.

Final

A final kulcsszó véglegest jelent, amelynek helytől függően különböző jelentése lehet.

  • Final adattag: olyan adattag, amelynek a kezdeti inicializálása után az értéke nem változhat meg. Final adattagnak lehet kezdőértéket adni a deklaráció helyén is, valamint a konstruktorban is, de csak egyszer. UML-ben ezeket a tulajdonságokat általában a {readOnly} jelzővel látják el.
public class Macskafele {
    private String nev;
    private double suly;
    protected int bolhakSzama;
    private final String rendszertaniBesorolas;

    public Macskafele(String nev, double suly) {
        this.nev = nev;
        this.suly = suly;
        this.bolhakSzama = 0;
        this.rendszertaniBesorolas = "Felidae";
    }

    protected Macskafele(String nev, double suly, String rendszertaniBesorolas) {
        this.nev = nev;
        this.suly = suly;
        this.bolhakSzama = 0;
        this.rendszertaniBesorolas = rendszertaniBesorolas;
    }

    // ...
}

A final adattagok esetén igaz, hogy ha primitív típusú adatunk van, akkor annak az értéke nem változtatható meg a deklaráció után. Ugyanez igaz akkor is, ha nem primitív a final adattagunk, maga az adattag értéke nem módulhat, tehát ebben az esetben a referencia nem változhat meg. Azonban értelemszerűen maga az objektum módosulhat, például, ha van egy final változónk, ami egy Hűtőszekrényre mutat, akkor a hűtőszekrénybe pakolhatunk dolgokat be/ki, de nem "cserélhetjük le" a hűtőszekrényt.

  • Final metódus: a final metódusokat nem lehet felülírni a gyerekosztályban. Ennek akkor lehet értelme, ha a gyerekosztályban tiltani szeretnénk egy viselkedés felülírását, például most a Tigris osztály osszeverekszik metódusa lehet ilyen, hiszen bármilyen speciális tigriseink is lesznek, nem szeretnénk, ha a verekedésük másképp lenne implementálva.
public class Tigris {

    // ...

    public final void osszeverekszik(Tigris masik) {
        if (this.getSuly() > masik.getSuly()) {
            this.nyavog();
            System.out.println("A üvöltésemmel elzavartam a másik tigrist!");
        } else {
            masik.nyavog();
            System.out.println("A másik tigris üvöltésétől megijedtem, el kell menekülnöm...");
        }
    }

    // ...
}

Ezt követően ezt a metódust nem tudjuk felülírni például a KardfoguTigris osztályban, ha megpróbáljuk, akkor hibát kapunk.

  • Final osztály: A final osztályoknak nem lehet gyerekosztálya. Például a HaziMacska osztály lehet final, ha nem szeretnénk, hogy a házi macskáknak speciálisabb típusai lennének.

UML-ben final metódusokat és osztályokat általában a <<final>> jelzéssel látják el. A final kulcsszó használatáról bővebben itt és itt olvashatsz.

Konstans

Nincs rá külön kulcsszó, az eddigiekből merítve azonban könnyen kitalálható: valódi konstans létrehozása a static és final kulcsszavak együttes használatával, hiszen az így létrejövő változó: statikus lesz, bármely objektumpéldány esetén ugyanazt az egy konkrét adatot látjuk/módosítjuk; valamint a final miatt a kezdőérték nem változtatható meg.

Konstans változók nevét általában csupa nagybetűvel szoktuk írni, szóhatárnál aláhúzást teszünk.

public class Alma {
 public static final int ALMA_TOMEG = 10;
}
Ennek elérése kívülről:

int almaTomege = Alma.ALMA_TOMEG;
Override

Ahogy láthattuk is már, ha az IDE generál nekünk metódust, például toString metódust, a metódus fejléce felett láthatunk egy @Override sort. Ennek a gyakorlatban nincs hatása, el is hagyhatjuk, de meg is tarthatjuk, és célszerű is megtartani. Javaban a metódus felett lévő @ karakterrel kezdődő sorokat annotációknak hívjuk. Az @Override annotáció csak egy jelzés a fordító számára, hogy felüldefiniáltunk egy őstől örökölt metódust. Nagyon hasznos, ha véletlen módosítjuk az ősosztályt (átírjuk a függvény fejlécét, amit felüldefiniálunk), de a gyerek osztályban lévő metódus fejlécét nem igazítjuk hozzá, akkor fordítási hibát kapunk, mivel override-nak jelöljük a metódust, de nem definiálunk felül semmit.

Javadoc

A Javadocról már korábban is esett szó, de talán az osztályok esetében nyer értelmet. Segítségével a forráskódba írt kommentekből HTML alapú dokumentáció generálható. Egy példa a generált HTML oldalra. Használata egyszerű, a hagyományos kommentezés helyett a /** és */ közé írjuk a kommenteket, majd ezekben használhatunk különféle speciális hivatkozásokat, melyek az elkészült dokumentációban speciálisan jelennek meg. Az összes speciális hivatkozás és leírásuk elérhető ezen a linken. A Javadoc esetében az általános szabály, hogy az osztályokat, azok adattagjait, valamint a publikus metódusokat illendő dokumentációval ellátni. Azonban természetesen célszerű a private, protected láthatóságú metódusokat is dokumentálni.

Summary

Kapcsolatok:

  • "Has-A": Aggregation, aggregáció (tartalmaz, például a ház tartalmaz egy postaládát)
  • "Part-of": Composition, kompozíció (része, például a ház része egy szoba)
  • "Is-a": Inheritance, öröklődés (az-egy, például a fürdőszoba is egy szoba)

UML példák

Kutyás példa

A kabinet modellje Az Irinyi kabinet modellje

Videók

Feladatok

  1. Készítsd el a fenti Hűtő-Étel-Kompresszor példát. A diagramon szereplő dolgokat értelemszerűen implementáld le. A Hűtő-Étel aggregációhoz használj tömböt.

UML tervezési feladatok

  1. Modellezd a bankkártyás fizetést! A modellben szerepeljenek a következő osztályok, valamint a közöttük lévő kapcsolatok: megoldás első, második

    • Bank
    • Számla
    • Személy
    • Kártya
    • Terminál ( kétfajta: automata és POS )
  2. Modellezzük azt a mesevilágot, amelyben a szereplők a

    • Sárkányok
    • Lovagok és ahol tudjuk, hogy a Sárkányokat nagyban meghatározza Fejeik száma, amelyre persze nagy hatással van a lovagok fejetLevag tevékenysége.
  3. Készítsünk olyan osztálydiagramot, amin tantermek, géptermek és hozzájuk kapcsolódó dolgok szerepelnek. Legalább 7 osztály és többféle kapcsolat legyen az ábrán!

Kapcsolódó linkek

Oracle: Controlling Access to Members of a Class

Java access specifiers

Inheritance

Inheritance & Polymorphism

Enum Types


Utolsó frissítés: 2024-04-11 07:54:27