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).
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.
Kétfajta illesztő minta lehetséges:[1]
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é.
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 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ő 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á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
}
}
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 */
}
}
}
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.