Bővebben a problémáról
Először is szimuláljuk a régi rendszer viselkedését. Tegyük fel, hogy kifogásokat generál a munkából vagy az iskolából való késés miatt. Ehhez egyExcuse
felülettel rendelkezik, amely rendelkezik generateExcuse()
, likeExcuse()
és dislikeExcuse()
metódusokkal.
public interface Excuse {
String generateExcuse();
void likeExcuse(String excuse);
void dislikeExcuse(String excuse);
}
Az WorkExcuse
osztály ezt a felületet valósítja meg:
public class WorkExcuse implements Excuse {
private String[] excuses = {"in an incredible confluence of circumstances, I ran out of hot water and had to wait until sunlight, focused using a magnifying glass, heated a mug of water so that I could wash.",
"the artificial intelligence in my alarm clock failed me, waking me up an hour earlier than normal. Because it is winter, I thought it was still nighttime and I fell back asleep. Everything after that is a bit hazy.",
"my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia."};
private String [] apologies = {"This will not happen again, of course. I'm very sorry.", "I apologize for my unprofessional behavior.", "There is no excuse for my actions. I am not worthy of this position."};
@Override
public String generateExcuse() { // Randomly select an excuse from the array
String result = "I was late today because " + excuses[(int) Math.round(Math.random() + 1)] + "\\n" +
apologies[(int) Math.round(Math.random() + 1)];
return result;
}
@Override
public void likeExcuse(String excuse) {
// Duplicate the element in the array so that its chances of being chosen are higher
}
@Override
public void dislikeExcuse(String excuse) {
// Remove the item from the array
}
}
Teszteljük a példánkat:
Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
Kimenet:
"I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
I apologize for my unprofessional behavior.
Most képzelje el, hogy elindított egy kifogás-generáló szolgáltatást, statisztikákat gyűjtött, és észrevette, hogy a felhasználók többsége egyetemi hallgató. Ennek a csoportnak a jobb kiszolgálása érdekében felkért egy másik fejlesztőt, hogy hozzon létre egy olyan rendszert, amely kifejezetten az egyetemisták számára generál kifogásokat. A fejlesztőcsapat piackutatást végzett, kifogásokat rangsorolt, mesterséges intelligenciát kapcsoltak be, és integrálták a szolgáltatást forgalmi jelentésekkel, időjárás-jelentésekkel stb. Most már van egy könyvtára az egyetemisták számára kifogások generálására, de más felülettel rendelkezik: StudentExcuse
.
public interface StudentExcuse {
String generateExcuse();
void dislikeExcuse(String excuse);
}
Ennek a felületnek két módja van: generateExcuse
, amely kifogást generál, és dislikeExcuse
, amely megakadályozza, hogy a kifogás a jövőben ismét megjelenjen. A harmadik féltől származó könyvtár nem szerkeszthető, azaz nem módosíthatja a forráskódját. Most van egy rendszerünk, amely két osztályt valósít meg az Excuse
interfészt megvalósítva, és egy könyvtár SuperStudentExcuse
, amelynek egy osztálya megvalósítja az StudentExcuse
interfészt:
public class SuperStudentExcuse implements StudentExcuse {
@Override
public String generateExcuse() {
// Logic for the new functionality
return "An incredible excuse adapted to the current weather conditions, traffic jams, or delays in public transport schedules.";
}
@Override
public void dislikeExcuse(String excuse) {
// Adds the reason to a blacklist
}
}
A kód nem módosítható. A jelenlegi osztályhierarchia így néz ki: A rendszer ezen verziója csak az Excuse felülettel működik. Nem írhatja át a kódot: egy nagy alkalmazásban az ilyen módosítások végrehajtása hosszadalmas folyamattá válhat, vagy megtörheti az alkalmazás logikáját. Bevezethetnénk egy alapfelületet és bővíthetnénk a hierarchiát: Ehhez át kell neveznünk az Excuse
interfészt. De az extra hierarchia nem kívánatos komoly alkalmazásokban: egy közös gyökérelem bevezetése megtöri az architektúrát. Meg kell valósítania egy köztes osztályt, amely lehetővé teszi mind az új, mind a régi funkcionalitás használatát minimális veszteséggel. Röviden, szükség van egy adapterre .
Az adapter mintájának alapelve
Az adapter egy köztes objektum, amely lehetővé teszi, hogy egy objektum metódushívásait egy másik objektum megértse. Valósítsunk meg egy adaptert a példánkhoz, és hívjukMiddleware
. Adapterünknek olyan interfészt kell megvalósítania, amely kompatibilis az egyik objektummal. Legyen Excuse
. Ez lehetővé teszi Middleware
az első objektum metódusainak meghívását. Middleware
fogadja a hívásokat, és kompatibilis módon továbbítja azokat a második objektumhoz. Íme a Middleware
megvalósítás a generateExcuse
és dislikeExcuse
módszerekkel:
public class Middleware implements Excuse { // 1. Middleware becomes compatible with WorkExcuse objects via the Excuse interface
private StudentExcuse superStudentExcuse;
public Middleware(StudentExcuse excuse) { // 2. Get a reference to the object being adapted
this.superStudentExcuse = excuse;
}
@Override
public String generateExcuse() {
return superStudentExcuse.generateExcuse(); // 3. The adapter implements an interface method
}
@Override
public void dislikeExcuse(String excuse) {
// The method first adds the excuse to the blacklist,
// Then passes it to the dislikeExcuse method of the superStudentExcuse object.
}
// The likeExcuse method will appear later
}
Tesztelés (kliens kódban):
public class Test {
public static void main(String[] args) {
Excuse excuse = new WorkExcuse(); // We create objects of the classes
StudentExcuse newExcuse = new SuperStudentExcuse(); // that must be compatible.
System.out.println("An ordinary excuse for an employee:");
System.out.println(excuse.generateExcuse());
System.out.println("\n");
Excuse adaptedStudentExcuse = new Middleware(newExcuse); // Wrap the new functionality in the adapter object
System.out.println("Using new functionality with the adapter:");
System.out.println(adaptedStudentExcuse.generateExcuse()); // The adapter calls the adapted method
}
}
Kimenet:
An ordinary excuse for an employee:
I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
There is no excuse for my actions. I am not worthy of this position.
Using new functionality with the adapter:
Hihetetlen kifogás az aktuális időjárási viszonyokhoz, forgalmi dugókhoz vagy a tömegközlekedési menetrendek késéseihez igazítva. A generateExcuse
metódus egyszerűen átadja a hívást egy másik objektumnak, további változtatások nélkül. A dislikeExcuse
módszer megkövetelte, hogy először feketelistára tegyük a kifogást. A közbenső adatfeldolgozás képessége az oka annak, hogy az emberek szeretik az adaptermintát. De mi a helyzet a likeExcuse
metódussal, amely része a Excuse
felületnek, de nem része a StudentExcuse
felületnek? Az új funkció nem támogatja ezt a műveletet. Erre UnsupportedOperationException
a helyzetre találták ki. A rendszer dobja, ha a kért művelet nem támogatott. Használjuk. Így Middleware
néz ki az osztály új megvalósítása:
public class Middleware implements Excuse {
private StudentExcuse superStudentExcuse;
public Middleware(StudentExcuse excuse) {
this.superStudentExcuse = excuse;
}
@Override
public String generateExcuse() {
return superStudentExcuse.generateExcuse();
}
@Override
public void likeExcuse(String excuse) {
throw new UnsupportedOperationException("The likeExcuse method is not supported by the new functionality");
}
@Override
public void dislikeExcuse(String excuse) {
// The method accesses a database to fetch additional information,
// and then passes it to the superStudentExcuse object's dislikeExcuse method.
}
}
Első ránézésre nem tűnik túl jónak ez a megoldás, de a funkcionalitás utánzása bonyolíthatja a helyzetet. Ha a kliens odafigyel, és az adapter jól dokumentált, akkor egy ilyen megoldás elfogadható.
Mikor kell adaptert használni
-
Amikor harmadik féltől származó osztályt kell használnia, de annak felülete nem kompatibilis a fő alkalmazással. A fenti példa bemutatja, hogyan hozható létre olyan illesztőobjektum, amely a hívásokat olyan formátumba csomagolja, amelyet a célobjektum megért.
-
Amikor több létező alosztálynak szüksége van valamilyen közös funkcióra. További alosztályok létrehozása helyett (ami a kód megkettőzéséhez vezet) jobb adaptert használni.
Előnyök és hátrányok
Előny: Az illesztő elrejti a kliens elől az egyik objektumtól a másikig terjedő kérések feldolgozásának részleteit. Az ügyfélkód nem foglalkozik az adatok formázásával vagy a célmetódus hívásainak kezelésével. Túl bonyolult, a programozók pedig lusták :) Hátrány: A projekt kódbázisát további osztályok bonyolítják. Ha sok inkompatibilis felülettel rendelkezik, a további osztályok száma kezelhetetlenné válhat.Ne keverje össze az adaptert a homlokzattal vagy a dekorátorral
Csak felületes vizsgálattal az adapter összetéveszthető a homlokzati és dekorációs mintákkal. Az adapter és a homlokzat közötti különbség az, hogy a homlokzat új interfészt vezet be, és az egész alrendszert beburkolja. És a dekorátor, ellentétben az adapterrel, magát az objektumot változtatja meg, nem pedig a felületet.Lépésről lépésre algoritmus
-
Először is győződjön meg arról, hogy van olyan problémája, amelyet ez a minta meg tud oldani.
-
Határozza meg a kliens felületet, amely az inkompatibilis objektumokkal való közvetett interakcióhoz használható.
-
Az adapter osztály örökölje az előző lépésben meghatározott interfészt.
-
Az illesztőosztályban hozzon létre egy mezőt az adaptee objektumra való hivatkozás tárolására. Ezt a hivatkozást átadjuk a konstruktornak.
-
Valósítsa meg az összes kliens interfész metódust az adapterben. Egy módszer lehet:
-
Továbbítsa a hívásokat változtatás nélkül
-
Adatok módosítása vagy kiegészítése, a célmódszer hívásainak számának növelése/csökkentése stb.
-
Szélsőséges esetekben, ha egy adott módszer továbbra is inkompatibilis, dobjon egy UnsupportedOperationException-t. A nem támogatott műveleteket szigorúan dokumentálni kell.
-
-
Ha az alkalmazás csak az adapterosztályt használja a kliens felületen keresztül (mint a fenti példában), akkor az adapter a jövőben fájdalommentesen bővíthető.
GO TO FULL VERSION