Illesztő programtervezési minta

A számítástudományban az illesztő programtervezési minta (gyakran becsomagoló minta vagy csak egyszerűen csomagoló vagy illesztő) (angolul Adapter) egy programtervezési minta, amely lefordítja az egyik osztály interfészét egy kompatibilis másik interfészre.[1] Egy illesztő lehetővé teszi olyan osztályok együttműködését, amelyek az inkompatibilis interfészeik miatt normálisan nem tudnának együttműködni, mindezt oly módon, hogy interfészt nyújt a kliensek számára, míg ő maga az eredeti interfészt használja. Az illesztő interfész hívásokat lefordítja az eredeti interfész hívásokra, tipikusan kis mennyiségű kód segítségével. Az illesztő felelős továbbá az adat megfelelő formátumba való áttranszformálásáért is. Például ha több boolean értéket tárolunk egyszerű integer-ként (azaz flag-ekként), de a kliens önálló boolean értékeket igényel, az illesztő felelős a megfelelő értékek előállításáért az integer értékből és vissza. Egy másik példa a dátum formátumok transzformációi (pl. YYYYMMDD-t MM/DD/YYYY-é vagy DD/MM/YYYY-é alakítás).

Definíció

[szerkesztés]

Az illesztő segíti két nem kompatibilis interfész együttműködését. Ez az illesztő valós életbeli definíciója. Az illesztő tervezési mintát akkor használjuk, amikor két különböző osztály nem kompatibilis interfészét szeretnénk együttműködésre bírni. Az elnevezés pusztán ennyit jelent. Az interfészek lehetnek nem kompatibilisek, de a belső funkcionalitás illeszkedik az igényekhez. Az illesztő minta lehetővé teszi a másként nem kompatibilis osztályok együttműködését azáltal, hogy konvertálja az egyik osztály interfészét a kliens által elvárt másik interfészébe.

A Struktúra

[szerkesztés]

Kétfajta illesztő minta lehetséges:[1]

Objektum illesztő minta

[szerkesztés]

Ebben a típusú illesztő mintában az illesztő tartalmazza annak az osztálynak egy példányát, amelyet becsomagol. Ebben a helyzetben az illesztő hívásokat indít a becsomagolt példány objektum felé.

Az objektum illesztő minta UML-ben. Az illesztő elrejti az illesztett interfészét a kliens elől
Az objektum illesztő minta LePUS3-ban

Osztály illesztő minta

[szerkesztés]

Ez a fajta illesztő több többalakú interfészt használ, hogy célját elérje. Az illesztő úgy készül, hogy megvalósítja vagy örökli mind várt interfészt, mind a már korábban létező interfészt.Tipikusan a várt interfész egy sima interfész osztály, különösen olyan programozási nyelvekben, mint a Java, amely nem támogatja a többszörös öröklődést.[1]

Az osztály illesztő minta UML-ben
Az osztály illesztő minta LePUS3-ban

Az illesztő minta azokban az esetek hasznos, amikor egy már létező osztály biztosítja a számunkra szükséges néhány vagy az összes szolgáltatást, de nem használja azt, az interfészt, amire szükségünk van. Egy valós életből vett példa: egy illesztő, amely egy XML dokumentum DOM interfészét alakítja át egy megjeleníthető fa struktúrára. A cikk alján lévő listában megadjuk azokat a oktatási célzatú hivatkozásokat, amelyek az illesztő programtervezési mintát használják.

A futásidejű illesztő minta további formái

[szerkesztés]

A futásidejű illesztő mintának még a további formái ismertek:

Tegyük fel, hogy a classA-nak kell ellátnia a classB-t adatokkal pl. String adattal. Erre egy fordítási idejű megoldás a következő:

classB.setStringData(classA.getStringData());

Habár feltételezzük, hogy a string formátumú adat megváltoztatható. Egy fordítási idejű megoldás az öröklődés használatára:

Format1ClassA extends ClassA {
   public String getStringData() {
      return format(toString());
   }
}

és esetleg elkészíti a korrekt formátumú objektumot futásidőben a Gyár programtervezési minta segítségével.

Egy megoldás illesztők használatával a következő:

(i) Definiáljunk egy köztes "szolgáltató" interfészt, és írjunk meg a szolgáltató implementációját, amely becsomagolja az adat forrást. Pl. ebben a példában ClassA, amely kiírja az adatokat a megfelelő formátumban:

public interface StringProvider {
    public String getStringData();
}

public class ClassAFormat1 implements StringProvider {
    private ClassA classA = null;

    public ClassAFormat1(final ClassA A) {
        classA = A;
    }

    public String getStringData() {
        return format(classA.toString());
    }
}

(ii) Írjunk egy illesztő osztályt, amely visszatér a szolgáltató egy speciális megvalósításával:

public class ClassAFormat1Adapter extends Adapter {
   public Object adapt(final Object OBJECT) {
      return new ClassAFormat1((ClassA) OBJECT);
   }
}

(iii) Regisztráljuk az Adapter-t a globális registry-ben, hogy az Adapter lookup-olni tudjon futásidőben:

AdapterFactory.getInstance().registerAdapter(ClassA.class, ClassAFormat1Adapter.class, "format1");

(iv) Abban az esetben, ha adatot szeretnénk küldeni a ClassA-ból a ClassB-ba, írjuk a következőt:

Adapter adapter = AdapterFactory.getInstance().getAdapterFromTo(ClassA.class,
    StringProvider.class, "format1");
StringProvider provider = (StringProvider) adapter.adapt(classA);
String string = provider.getStringData();
classB.setStringData(string);

vagy rövidebben:

classB.setStringData(((StringProvider) AdapterFactory.getInstance().getAdapterFromTo(ClassA.class,
    StringProvider.class, "format1").adapt(classA)).getStringData());

(v) Akkor előnyösebb, ha az adatátvitelhez a 2. formátum az elvárt: meg kell keresni hozzá a különböző illesztőt/szolgáltatót:

Adapter adapter = AdapterFactory.getInstance().getAdapterFromTo(ClassA.class,
    StringProvider.class, "format2");

(vi) Ha az elvárt viselkedés az, hogy a kimenet megkapható legyen a ClassA-ból, pl. képi adatként ClassC formájába:

Adapter adapter = AdapterFactory.getInstance().getAdapterFromTo(ClassA.class, ImageProvider.class,
    "format1");
ImageProvider provider = (ImageProvider) adapter.adapt(classA);
classC.setImage(provider.getImage());

(vii) Ezzel a módszerrel az illesztők és szolgáltatók használata biztosítja a ClassB-n és ClassC-n keresztül a többszörös "nézeteket" a ClassA-on az osztály hierarchia módosítása nélkül. Általánosan szólva így egy mechanizmus engedélyezhető önkényes adatfolyamokra az objektumok között, ami így utólagosan alkalmazható egy létező objektum hierarchiára.

Az illesztő minta megvalósítása

[szerkesztés]

Az illesztő minta megvalósításakor az egyértelműség kedvéért használjuk az [AdapteeClassName]To[Interface]Adapter osztály neveket, pl. DAOToProviderAdapter. Kell egy konstruktor metódus, aminek az illesztendő osztály változó paramétere. Ezt a paramétert fogja tovább adni a AdapteeClassName]To[Interface]Adapter példány változónak.

public class AdapteeToClientAdapter implements Client {

    private final Adaptee instance;

    public AdapteeToClientAdapter(final Adaptee instance) {
         this.instance = instance;
    }

    @Override
    public void clientMethod() {
       // call Adaptee's method(s) to implement Client's clientMethod
    }

}
public class AdapteeToClientAdapter implements Adapter {

    private final Adaptee instance;

    public AdapteeToClientAdapter(final Adaptee instance) {
         this.instance = instance;
    }

    @Override
    public void clientMethod() {
       // call Adaptee's method(s) to implement Client's clientMethod
    }

}

Az illesztő minta C# megvalósítása

[szerkesztés]

A következő példa az illesztő minta egy C# megvalósítást mutatja be.[2] Ebben a példában IEmployee objektumok gyűjteményét (collection) készítjük el. Habár az employee (alkalmazott) megvalósítja az IEmployee-t, így természetesen hozzáadható a listához, de a Consultant (tanácsadó) osztály nem kötődik az IEmployee-hoz. Azért, hogy ez az osztály hozzáadható legyen ugyanahhoz a listához, el kell készíteni egy EmployeeAdapter nevű illesztő osztályt, amely becsomagolja a Consultant osztályt. Ez használja az IEmployee interfészt, ezáltal lehetővé téve, hogy hozzáadható legyen ugyanahhoz a listához.

using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        List<IEmployee> list = new List<IEmployee>();
        list.Add(new Employee("Tom"));
        list.Add(new Employee("Jerry"));
        list.Add(new ConsultantToEmployeeAdapter("Bruno"));  //consultant from existing class
        ShowHappiness(list);
    }

    //*** Code below from the existing library does not need to be changed ***
    static void ShowHappiness(List<IEmployee> list)
    {
        foreach (IEmployee i in list)
            i.ShowHappiness();
    }
}

//from the existing library, does not need to be changed
public interface IEmployee
{
    void ShowHappiness();
}

public class Employee : IEmployee
{
    private string name;
    
    public Employee(string name)
    {
        this.name = name;
    }

    void IEmployee.ShowHappiness()
    {
        Console.WriteLine("Employee " + this.name + " showed happiness");
    }
}

//existing class does not need to be changed
public class Consultant
{
    private string name;

    public Consultant(string name)
    {
        this.name = name;
    }

    protected void ShowSmile()
    {
        Console.WriteLine("Consultant " + this.name + " showed smile");
    }
}

public class ConsultantToEmployeeAdapter: Consultant, IEmployee
{
    public ConsultantToEmployeeAdapter(string name) : base(name)
    {
    }

    void IEmployee.ShowHappiness()
    {
        base.ShowSmile();  //call the parent Consultant class
    }
}
implicit def adaptee2Adapter(adaptee: Adaptee): Adapter = {
  new Adapter {
    override def clientMethod: Unit = {
    // call Adaptee's method(s) to implement Client's clientMethod */
    }
  }
}

Kapcsolódó szócikkek

[szerkesztés]

Jegyzetek

[szerkesztés]
  1. a b c (2004) „Head First Design Patterns” (paperback), 244. o, Kiadó: O'Reilly Media. [2013. május 4-i dátummal az eredetiből archiválva]. (Hozzáférés: 2013. április 30.) 
  2. Thank you to devshed for providing the original version of this code under open license

További információk

[szerkesztés]
Az angol Wikikönyvekben
további információk találhatók

Fordítás

[szerkesztés]

Ez a szócikk részben vagy egészben az Adapter 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.