CodeGym /Java blog /Véletlen /Milyen problémákat old meg az adapter tervezési mintája?
John Squirrels
Szint
San Francisco

Milyen problémákat old meg az adapter tervezési mintája?

Megjelent a csoportban
A szoftverfejlesztést megnehezítik az inkompatibilis összetevők, amelyeknek együtt kell működniük. Például, ha egy új könyvtárat kell integrálnia egy régi, a Java korábbi verzióiban írt platformmal, akkor összeférhetetlen objektumokkal, vagy inkább inkompatibilis felületekkel találkozhat. Milyen problémákat old meg az adapter tervezési mintája?  - 1Mi a teendő ebben az esetben? Átírni a kódot? Ezt nem tehetjük meg, mert a rendszer elemzése sok időt vesz igénybe, vagy az alkalmazás belső logikája sérül. A probléma megoldására létrehozták az adaptermintát. Segíti az inkompatibilis interfésszel rendelkező objektumok együttműködését. Lássuk, hogyan kell használni!

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 egy Excusefelülettel rendelkezik, amely rendelkezik generateExcuse(), likeExcuse()és dislikeExcuse()metódusokkal.

public interface Excuse {
   String generateExcuse();
   void likeExcuse(String excuse);
   void dislikeExcuse(String excuse);
}
Az WorkExcuseosztá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 Excuseinterfészt megvalósítva, és egy könyvtár SuperStudentExcuse, amelynek egy osztálya megvalósítja az StudentExcuseinterfé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: Milyen problémákat old meg az adapter tervezési mintája?  - 2A 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: Milyen problémákat old meg az adapter tervezési mintája?  - 3Ehhez át kell neveznünk az Excuseinterfé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ívjuk Middleware. Adapterünknek olyan interfészt kell megvalósítania, amely kompatibilis az egyik objektummal. Legyen Excuse. Ez lehetővé teszi Middlewareaz első objektum metódusainak meghívását. Middlewarefogadja a hívásokat, és kompatibilis módon továbbítja azokat a második objektumhoz. Íme a Middlewaremegvalósítás a generateExcuseés dislikeExcusemó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 generateExcusemetódus egyszerűen átadja a hívást egy másik objektumnak, további változtatások nélkül. A dislikeExcusemó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 likeExcusemetódussal, amely része a Excusefelületnek, de nem része a StudentExcusefelületnek? Az új funkció nem támogatja ezt a műveletet. Erre UnsupportedOperationExceptiona helyzetre találták ki. A rendszer dobja, ha a kért művelet nem támogatott. Használjuk. Így Middlewarené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

  1. 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.

  2. 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

  1. Először is győződjön meg arról, hogy van olyan problémája, amelyet ez a minta meg tud oldani.

  2. Határozza meg a kliens felületet, amely az inkompatibilis objektumokkal való közvetett interakcióhoz használható.

  3. Az adapter osztály örökölje az előző lépésben meghatározott interfészt.

  4. 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.

  5. 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.

  6. 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ő.

Természetesen ez a tervezési minta nem csodaszer minden bajra, de segíthet elegánsan megoldani a különböző interfésszel rendelkező objektumok közötti inkompatibilitás problémáját. Az alapmintákat ismerő fejlesztő több lépéssel előrébb jár, mint azok, akik csak algoritmusokat tudnak írni, mert komoly alkalmazások létrehozásához tervezési minták szükségesek. A kód újrafelhasználása nem olyan nehéz, a karbantartás pedig élvezetessé válik. Ez minden mára! De hamarosan folytatjuk a különféle tervezési minták megismerését :)
Hozzászólások
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION