Paveldimumas ir kompozicija objektiniame programavime  

Objektinis programavimas (OOP) turi dvi koncepcijas, leidžiančias pakartotinai panaudoti kodą: paveldimumą ir kompoziciją.

Paveldimumas ()in yra mechanizmas, kai nauja klasė (poklasė) paveldi esamos klasės (viršklasės) savybes ir elgseną. Pvz. klasė Automobilis yra klasės Transportas tipo. „Java“ kalboje paveldimumas nurodomas extends raktinį žodį. Poklasė paveldi viešus laukus ir metodus, leisdama pakartotinai panaudoti esamą kodą ir loginę hierarchiją.

class Transportas {
    String firma;
    int greitis;

    void greitinti() {
        greitis += 10;
    }

    @Override
    void displayInfo() {
        System.out.println("Firma: " + firma + "\nMaksimalus greitis: " + greitis);
    }
}

class Automobilis extends Transportas {
    int durys;

    @override
    void displayInfo() {
        System.out.println("Firma: " + firma + ", Greitis: " + greitis + ", Durys: " + durys);
    }
}

Paveldimumo privalumai:
kodo pakartotinas panaudojamumas: bendras funkcionalumas parašomas vienąkart viršaibėje ir panaudojamas daugelio poklausių;
Loginė hierarchija: gali būti sukurta realaus pasaulio santykių hierarchija.

Paveldimumo trūkumai: Stiprus susietumas: Poklasės stipriai susietos su viršklasėmis, apsunkinant kodo keitimą ir išplėtimą;
Jautrumas: Pakeitimai viršklasėje gali nenumatytai paveikti poklasių elgseną.

Kompozicija leidžia sukurti sudėtingus tipus, panaudojant ir derinant kitų tipų objektus, dažniausiai per klasės laukus. Tai leidžia didesnį lankstumą ir sumažina priklausomybę tarp klasių.

class Variklis {
    int galia;

    void info() {
        System.out.println("Variklio galia " + galia + " HP.");
    }
}

class Automobilis {
    Variklis variklis;

    Automobilis(Variklis pVariklis) {
        this.variklis = pVariklis;
    }

    void apie() {
        variklis.info();
    }
}

Paveldimumo privalumai:
Lankstumas: galima pakeisti ar išplėsti komponentus nepaveikiant visos sistemos;
Atsisakyta susietumo: komponentės gali būti laisvai grupuojamos užtikrinant kodo moduliarumą ir paprastesnį testavimą.

Paveldimumo trūkumai:
Daugiau kodo;
Didesnis sudėtingumas: Reikia prižiūrėti daugiau objektų.

Kada naudotinas paveldimumas:
Kai yra aiški hierarchinė priklausomybė, pvz., Pastatas – Namas – Kambarys;
Kai reikia kitaip apibrėžti (override) ar išplėsti funkcionalumą.

Kada naudotina kompozicija:
Kai reikia lankstesnės struktūros, o sistema linkusi keistis ar vystytis;
Kai svarbiau nepriklausomumas ir elgsenos įkapsuliavimas.

Java ir polimorfizmas    

Polimorfizmas - tai objekto gebėjimas vykdyti veiksmus, atsižvelgiant į jo tipą. Visa tai padaro programos kodą lankstesniu.

Pirmiausia dėmesį atkreipsime į polimorfizmo ryšį su paveldimumu (inheritance) - polimorfizmas reikalauja paveldimumo arba interfeiso realizacijos. Tai iliustruojama Didiko ir Vergo pavyzdžiu:

public abstract class Titulas {
    public abstract void executeAction();
}
public class Didikas extends Titulas {
    @Override
    public void executeAction() {
        System.out.println("Plak!");
    }
}
public class Vergas extends Titulas {
    @Override
    public void executeAction() {
        System.out.println("Spruk!");
    }
}
public class Testas {
    public static void main(String... argumentai) {
        Titulas didikas = new Didikas();
        Titulas vergas = new Vergas();
        didikas.executeAction();
        vergas.executeAction();
    }
}

Jos rezultatas bus

Plak!
Spruk!

Polimorfizmo tikslas yra atsieti kliento klasę nuo jos realizacijos – ir klientų klasė žino tik tiek, kad galėtų atlikti savo veiksmus. Kad geriau suprastumėm tai, ažiūrėkime į Konditeris klasę:

public abstract class Konditeris {
    public abstract void produceSweet();
}
public class Tortas extends Konditeris {
    @Override
    public void gaminti() {
        System.out.println("Pagamintas tortas");
    }
}
public class Saldainis extends Konditeris {
    @Override
    public void gaminti() {
        System.out.println("Pagamintas saldainis");
    }
}
public class Sausainis extends Konditeris {
    @Override
    public void gaminti() {
        System.out.println("Pagamintas sausainis");
    }
}
public class Gamintojas {
    private List produktai;
    public Gamintojas(List produktai) {
        this.produktai = produktai;
    }
    public void gaminti() {
        produktai.forEach(gaminys -> gaminys.gaminti());
    }
}
public class Testas {
    public static void main(String... argumentai) {
        Gamintojas produktai = new Konditeris(Arrays.asList(new Tortas(),
                new Saldainis(), new Sausainis()));
        produktai.gaminti();
    }
}

Šiame pavyzdyje Konditeris klasė težino tik Gamintojas klasę, bet ne jos realizaciją kiekvienam Produktui.

Pastaba: @Override anotacija įpareigoja naudoti tą patį metodą, kuris yra perdengiamas (naujai apibrėžiamas) – kitaip gausime kompiliavimo klaidą.

Vis tik nemažai programuotojai painioja polimorfizmo ryšį su metodo perdengimu (override) ir metodo apkrovimu(overloading). Tik perdengimas yra poliformizmas, o metodo apkrovimas yra metodo tuo pačiu pavadinimu , tačiau su skirtingais parametrų tipais naudojimas.

Netgi įmanoma pakeisti gražinamos reikšmės tipą perdengiamam metodui, jei jis yra kovariantiniu tipu, t.y., iš esmės, gražinamo tipo poklase. Pvz.,

public abstract class Titulas {
    abstract Titulas get();
}
public class Didikas extends Titulas {
    @Override
    Didikas get() {
        return new Didikas();
    }
}

Galima ir kviesti specifinius metodus polimorfiniame iškvietime, tačiau tuo prarandame lankstumą. Tam panaudojama kastingo (casting) technika. Pvz.,

public abstract class Karys {
    abstract void naudoti(String ginklas);
    abstract String get();
}
public class Tankistas extends Karys {
    @Override
    void naudoti(String ginklas) {
        System.out.println("Tankistas naudoja " + ginklas);
    }
     String get() {
	   return "patranka";
     }
   void Liepk(String komanda) {
        System.out.println(komanda);
    }
}
public class Eilinis extends Karys {
    void naudoti(String ginklas) {
        System.out.println("Eilinis naudoja " + ginklas);
    }
     String get() {
	   return "automatas";
     }
}
public class UseSpecificMethod {
    public static void Vykdyk( Karys agentas ) {
        agentas.naudoti(agentas.get());
    
        if ( agentas instanceof Tankistas ) {
            ((Tankistas)agentas).Liepk("Pulkime kartu!");
        }
    }

public static void main(String... specifinisKvietimas) {
        Vykdyk(new Eilinis());
        Vykdyk(new Tankistas());
    }
}

Jos vykdymo rezultatu bus

Eilinis naudoja automatas
Tankistas naudoja patranka
Pulkime kartu!


(c) 2024, Vartiklis. Visos teisės saugomos. Leidžiama naudoti tik asmeninės savišvietos tikslais. Bet koks platinimas bet kokiomis priemonemis, viso teksto arba atskiros jo dalies, draudžiamas!

Tiesiog - Java
'Java' ir ne tik ji!
Anotacijos Java kalboje
Skriptai - ateities kalbos?
JavaScript pradžiamokslis
Java 8: Optional prieš null
Programavimas Unix aplinkoje
Programavimo kalbų klegesys
Pitonas, kandantis sau uodegą!
AWK kalba - sena ir nuolat aktuali
Lambda išraiškos – Java į naują lygį
Pirmasis „Java“ įskiepis Lietuvoje
CGI.pm biblioteka: sausainiai
Programavimo kalbų istorija
Vaizdi rašysena - VB Script
Dygios JavaScript eilutės
Iš kur Javos tas lėtumas?
Unix komandinė eilutė
Ruby on Rails
AdvancedHTML
Vartiklis