Szolga programtervezési minta
A szolga egy programtervezési minta, amit akkor használunk, ha osztályok egy csoportja számára szeretnénk néhány funkciót készíteni anélkül, hogy minden funkciót hozzájuk rendelnénk egyesével. A Szolga egy olyan osztály, melynek példánya (vagy csak maga az osztály) olyan metódusokat biztosit, melyek a kívánt szolgáltatást ellátják, miközben az objektumok, melyeknek (vagy melyekkel) a szolga tesz valamit, paraméterként jelennek meg.
Leírás és egyszerű példa
[szerkesztés]Szolgát használunk, ha osztályok valamilyen csoportja számára valamilyen viselkedést szeretnénk biztosítani. Ahelyett, hogy ezt a viselkedést minden osztályban külön definiálnánk – vagy amikor nem tudjuk ezt a viselkedést alkalmazni a közös ős osztály esetén – csak egyszer definiálunk, a szolgában.
Például: van néhány osztályunk, melyek geometriai objektumokat jelölnek (téglalap, ellipszis és háromszög). Ábrázolhatjuk ezeket vásznon. Amikor 'mozgatás' metódust szeretnénk adni ezeknek az objektumoknak lehetőségünk van minden osztályban külön implementálni ezt a metódust, vagy megadhatunk egy implementálandó interfészt, majd felajánlhatjuk a szolga 'mozgatás' metódusát. Meghatározunk egy interfészt, hogy biztosítsuk a kiszolgált osztályoknak vannak olyan metódusai, melyre a szolgának szüksége van a kívánt viselkedés biztosításához. Ha folytatjuk korábbi példánkat, létrehozunk egy 'Movable' interfészt, mely megszabja, hogy minden osztályban, mely ezt az interfészt implementálja, legyen egy 'getPosition' és egy 'setPosition' metódus. Az első metódus feladata, hogy megszerezze a vásznon lévő objektum pozícióját, míg a második meghatározza az objektum elhelyezkedését és megrajzolja azt a vásznon. Majd létrehozunk egy 'MoveServant' szolga osztályt, melynek két metódusa lesz: 'moveTo(Moveable movedObject, Position where)' és 'MoveBy(moveable movedObect, int dx, int dy). Most már használhatjuk a szolga osztályt, hogy mozgassunk minden objektumot, mely implementálja a Moveable-t. Így a 'moving' kód csak egy osztályban jelenik meg a kódban, mely tiszteletben tartja a 'Separation of Concerns' szabályt.
Kétféle megvalósítási mód
[szerkesztés]Két lehetséges mód van, hogy implementáljuk ezt a tervezési mintát.
- a felhasználó ismeri a szolgát (ebben a esetben nem szükséges ismernie a kiszolgált osztályt) és üzeneteket küld a kérésekről a szolga példányoknak, mely paraméterként tartalmazza a kiszolgált objektumokat
- a kiszolgált osztályok ismerik a szolgát és a felhasználó üzeneteket küld nekik a kérésekről (ebben az esetben nem kell ismernie a szolgát). A kiszolgált példányok ekkor üzenetet küldenek a szolgapéldányoknak, szolgáltatást kérve.
- A kiszolgált osztályok (geometriai objektumok a korábbi példánkból) nem ismerik a szolgát, de implementálják az 'IServiced' interfészt. A felhasználói osztály csak meghívja a szolga egy metódusát és paraméterként küldi a kiszolgált objektumokat. Ezt mutatja az első ábra
- A második ábrán az ellenkező szituáció látható, ahol a felhasználó nem ismeri a szolga osztályt és közvetlenül a kiszolgált osztályt hívja meg. Ekkor a kiszolgált osztályok magát a szolgát kérik meg, hogy érje el a kívánt funkciót.
A szolga implementációja
[szerkesztés]- Elemezd a viselkedést, amiről a szolgának gondoskodnia kell. Állítsd össze, hogy a szolga milyen metódusokat fog meghatározni és ezek a metódusok mit fognak elvárni a kiszolgált paraméterektől. Más szavakkal, mit kell a kiszolgált példányoknak biztosítani, hogy a szolgák elérhessék céljaikat.
- Elemezd, milyen képességekkel kell a kiszolgált osztályoknak bírnia, hogy megfelelően legyenek kiszolgálva.
- Készíts egy interfészt, mely érvényesíti a meghatározott metódusok végrehajtását.
- Készíts egy interfészt, mely megadja a kiszolgált objektumok elvárt viselkedését. Ha valamely példány szeretné, hogy kiszolgálják, akkor implementálnia kell a szolgát.
- Készíts (vagy szerezz valahonnan) specifikált szolgát (azaz az osztályát).
- Implementáld a meghatározott interfészt a kiszolgált osztállyal.
Példa
[szerkesztés]Ez az egyszerű példa bemutatja a fent leírt szituációt. Csak illusztráció és nem teszti lehetővé az objektumok tényleges kirajzolását, vagy annak meghatározását, hogy hogyan néznek ki.
// Szolga osztály, mely a Movable interface-t implementáló osztályoknak ajánlja szolgáltatásait
public class MoveServant {
// Függvény, mely átmozgatja a Movable implementáló interfészt olyan pozícióba, ahol
public void moveTo(Movable serviced, Position where) {
// Itt további funkciókat adhatunk meg, melynek segítségével a mozgatás szebbé tehető
serviced.setPosition(where);
}
// Függvény, mely elmozgatja a Movable implementációs osztályt dx és dy értékkel
public void moveBy(Movable serviced, int dx, int dy) {
// Itt további funkciókat adhatunk meg
dx += serviced.getPosition().xPosition;
dy += serviced.getPosition().yPosition;
serviced.setPosition(new Position(dx, dy));
}
}
// Interfész, mely meghatározza, hogy mely kiszolgált osztályokat kell implementálni, kiszolgálni a Szolgának.
public interface Movable {
public void setPosition(Position p);
public Position getPosition();
}
// Az egyik geometriai osztály
public class Triangle implements Movable {
// A geometriai alakzat helye valamely vásznon
private Position p;
// Függvény, mely átállítja a geometriai alakzat pozícióját
public void setPosition(Position p) {
this.p = p;
}
// Függvény, mely visszaadja a geometriai alakzat pozícióját
public Position getPosition() {
return this.p;
}
}
// A másik geometriai osztály
public class Ellipse implements Movable {
// A geometriai alakzat helye valamely vásznon
private Position p;
// Függvény, mely átállítja a geometriai alakzat pozícióját
public void setPosition(Position p) {
this.p = p;
}
// Függvény, mely visszaadja a geometriai alakzat pozícióját
public Position getPosition() {
return this.p;
}
}
// A harmadik geometriai osztály
public class Rectangle implements Movable {
// A geometriai alakzat helye valamely vásznon
private Position p;
// Függvény, mely átállítja a geometriai alakzat pozícióját
public void setPosition(Position p) {
this.p = p;
}
// Függvény, mely visszaadja a geometriai alakzat pozícióját
public Position getPosition() {
return this.p;
}
}
// Egyszerű konténer osztály a pozicionáláshoz
public class Position {
public int xPosition;
public int yPosition;
public Position(int dx, int dy) {
xPosition = dx;
yPosition = dy;
}
}
Parancs: egy hasonló tervezési minta
[szerkesztés]Két nagyon hasonló tervezési minta a szolga és parancs, olyannyira, hogy megvalósításuk gyakran látszólag teljesen azonos. A kettő között a különbség a megközelítésben található.
- A szolga mintában van néhány objektumunk, melyek számára valamilyen funkciókat szeretnénk felajánlani. Készítünk egy osztályt, melynek példányai felajánlják ezeket a funkciókat és mely meghatároz egy interfészt, melyet a kiszolgált objektumoknak implementálni kell. A kiszolgált példányokat gyakran paraméterként küldik a szolgának.
- A parancs metódusban van néhány objektumunk, melyet valamilyen függvény segítségével módosítani szeretnénk. Ezért létrehozunk egy interfészt, mely megparancsolja, hogy milyen funkciókat kell implementálni. Ezután ezeknek a parancsoknak a példányait saját függvényeik paramétereként küldjük el az eredeti objektumoknak.
Annak ellenére, hogy ez a két tervezési minta nagyon hasonló, ez nem feltétlenül igaz minden esetben. Számos olyan eset ismert, ahol a parancs minta alkalmazása teljesen eltér a szolga mintától. Ezekben az esetekben a meghívott függvénynek csak egy másik függvényre hivatkozó referenciát kell átadni, mely segít a célunk elérésében. Mivel sok nyelvben nem adhatunk referenciát függvényeknek (pl Java), a referencia helyett egy olyan osztályt kell megalkotnunk, mely implementál egy interfészt, amely elfogadja az átadott metódus aláírását.
Kapcsolódó szócikkek
[szerkesztés]Források
[szerkesztés]Pecinovský, Rudolf (2006. június 1.). „Let’s Modify the Objects First Approach into Design Patterns First”. Eleventh Annual Conference on Innovation and Technology in Computer Science Education, University of Bologna.
Fordítás
[szerkesztés]Ez a szócikk részben vagy egészben a Servant (design pattern) című angol Wikipédia-szócikk ezen változatának fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.