Kihagyás

Szerkezeti minták

Az előadás videója elérhető itt.

A szerkezeti mintákat szintén minden területén használhatjuk a szoftverfejlesztésnek. Céljuk az, hogy objektumok, vagy osztályok összetételével nagyobb szerkezeteket hozzunk létre, amelyek kezelése így áttekinthetőbb.

Adapter

Az adapter tervezési mintát akkor használjuk, ha egy adott kód interfészét egy olyan interfésszel szeretnénk használni, amit a kliens ismer. Általában ez akkor kellhet, ha egy meglevő rendszerhez akarunk hozzáigazítani egy másikat. Motivációs példa éppen ezért lehet az, hogy egy grafikus szerkesztőben akarunk használni egy olyan meglevő könyvtárat, modult, amely már használatos volt mondjuk egy szövegszerkesztőben, de valamiért az alapból nem alkalmazható, mert nem megfelelő az interfésze.

adapter

A mintában a Client a rendszert csak egy alkalmazás specifikus interfészen keresztül látja. A konkrét viselkedést, amit a kliens valójában működtetni szeretne, az az Adaptee specificRequest operációja, amely interfészét azonban a kliens nem ismeri. Ezért kell az Adapter osztály, ami vagy definiálja a Target osztály request metódusát úgy, hogy abban gyakorlatilag meghívja az az Adaptee specificRequest metódusát oly módon, hogy származik az Adaptee osztályból, így ezt simán meg tudja tenni, hisz elérhető számára ez a metódus (statikus megközelítés), vagy az Adapter osztály létrehoz egy adaptee objektumot, és azon keresztül hívja meg az Adaptee specificRequest metódusát (dinamikus megközelítés).

Composite

A Composite minta célja rész-egész szerkezetek leképezése egy objektum-hierarchiára (fa-szerkezetre), miközben a kliens a rész és egész egységeket azonosan kezeli.

Motiváció lehet egy rajzszerkesztő használata, amelyben primitív elemekből építünk fel összetett elemeket (akár rekurzívan), és célunk, hogy közben az összetett elemeket hasonlóan tudjuk kezelni, mint a rész elemeket (és fordítva). Azaz független attól, hogy összetett elemmel, vagy rész elemmel van dolgunk, ugyanolyan viselkedésekkel ruházhatjuk fel az elemeket. Az összetett elemek annyival lesznek többek, hogy azokhoz elemeket tudunk hozzátenni, illetve elemeket tudunk törölni. Viselkedésüket pedig úgy definiálhatjuk, hogy azok minden gyerek elemére alkalmazzuk az adott viselkedést. A lényeg, hogy a kliensnek nem kell tudnia, hogy összetett, vagy primitív elemmel van dolga, mind a kettőt ugyanúgy tudja kezelni.

composite

A minta résztvevői a Client, amely a rendszert a Component interfészen keresztül éri el és manipulálja, a Component, amely interfészt deklarál a hierarchia elérésére, definiálja azt a közös viselkedést, amit a kliens lát.

A Componentnek két speciális fajtája lehet. A Leaf az, ami a kompozíció primitív eleme lesz. Ebben definiálni kell azt, hogy a primitív elem az adott viselkedéseket konkrétan hogyan valósítja meg. A Composite definiálja az összetett objektumok viselkedését, tárolja a gyerek komponenseket és implementálja a rájuk vonatkozó interfészt.

Amennyiben egy kérés közvetlen egy Leaf elemre vonatkozik, akkor a kérést az közvetlen végrehajtja. Amennyiben a kérés egy Composite elemre vonatkozik, akkor az a gyerekeinek továbbítja a kérést, esetlegesen persze egyéb tevékenységeket is végrehajthat.

Összességében egy egyszerű rekurzív fa szerkezet alakul ki, amelyet a kliens egységesen kezel. Új elemeket könnyű hozzáadni, hiszen egy új elem vagy egy Leaf, vagy egy Composite speciális elem lehet. Ugyanakkor túl általánossá is válhat a rendszer, és ez igényelheti a futás közbeni típusellenőrzéseket, amik viszont lassíthatják a program futását.

Composite példa

A példánkban vegyük az Informatikai Intézetet (Composite) és annak tanszékeit (Leaf) alapul. Tegyük fel, hogy adatokat szeretnénk kiírni ezekről. Mind az Intézet, mind a tanszékek egy-egy szervezeti egységei (Component) lesznek az egyetemnek, de az Intézet egy összetett egység, amelyet több kisebb tanszék alkot. Az Intézethez természetesen lehet hozzáadni tanszékeket (esetleg törölni is), illetve ha egy operációt (jelen esetben az adatok kiírását) szeretnénk megvalósítani, azt tudjuk megtenni, hogy az őt alkotó tanszékek elemeit íratjuk ki.

import java.util.ArrayList;
import java.util.List;

public class Institute implements Department {
    private String name;
    private List<Department> childDepartments = new ArrayList<>();

    public void printDepartmentName() {
        childDepartments.forEach(Department::printDepartmentName);
    }

    public void addDepartment(Department department) {
        childDepartments.add(department);
    }

    public void removeDepartment(Department department) {
        childDepartments.remove(department);
    }
}
public class SoftwareEngineering implements Department {
    private String name = "Department of Software Engineering";
    public void printDepartmentName() {
        System.out.println(name);
    }
}
public class TechnicalInformatics implements Department {
    private String name = "Department of Technical Informatics";
    public void printDepartmentName() {
        System.out.println(name);
    }
}
public class CompositeDemo {
    public static void main(String args[]) {
        Department sed = new SoftwareEngineering();
        Department ti = new TechnicalInformatics();

        Institute headDepartment = new Institute();
        headDepartment.addDepartment(sed);
        headDepartment.addDepartment(ti);

        headDepartment.printDepartmentName();
    }
}

Kimenet

Department of Software Engineering
Department of Technical Informatics

Decorator

A Decorator tervezési minta célja újabb felelősségek/tulajdonságok dinamikus csatolása az objektumhoz. Motiváció lehet, hogy további funkcionalitást akarunk rendelni egyes objektumhoz, DE nem az összeshez, így nem akarjuk a teljes osztályt megváltoztatni. Bár ezt meg lehetne valósítani öröklődéssel is, de futásidőben az objektum típusát nem változtathatjuk, a különböző dekorátorokat viszont cserélgethetjük. A dekorálás során tulajdonképpen egy dekoráló objektumba csomagoljuk az eredeti objektumot, és kiegészítjük azt új funkcionalitásokkal, de az eredeti objektum viselkedését elérhetővé tesszük ezen objektumon keresztül. Minden olyan esetben alkalmazható, amikor a származtatás nem lehetséges, vagy nem praktikus.

decorator A dekorátor mintában a Component interfészt definiál az objektumokhoz, amelyekhez később, dinamikusan további felelősségeket kapcsolhatunk. Deklarálja azokat az operációkat, amelyeket a Decoratoron keresztül is elérhetővé szeretnénk tenni a kliens számára. A ConcreteComponent definiálja azt az objektumot, amelyhez további felelősséget kapcsolnánk. A Decorator objektum egy hivatkozást tárol a Component objektumra és vele megegyező interfészt definiál. A ConcreteDecoratorok újabb felelősségeket csatolhatnak a tartalmazott objektumhoz, amely lehet egy plusz operáció, vagy attribútum, de akár a meglévő viselkedések is módosíthatóak.


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