
Mai multe despre problema
Mai întâi, vom simula comportamentul vechiului sistem. Să presupunem că generează scuze pentru a întârzia la serviciu sau la școală. Pentru a face acest lucru, are oExcuse
interfață care are generateExcuse()
și metode. likeExcuse()
dislikeExcuse()
public interface Excuse {
String generateExcuse();
void likeExcuse(String excuse);
void dislikeExcuse(String excuse);
}
Clasa WorkExcuse
implementează această interfață:
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
}
}
Să testăm exemplul nostru:
Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
Ieșire:
"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.
Acum imaginați-vă că ați lansat un serviciu de generare de scuze, ați colectat statistici și ați observat că majoritatea utilizatorilor dvs. sunt studenți. Pentru a servi mai bine acest grup, ați cerut unui alt dezvoltator să creeze un sistem care să genereze scuze special pentru studenții universitari. Echipa de dezvoltare a efectuat cercetări de piață, a clasat scuzele, a conectat ceva inteligență artificială și a integrat serviciul cu rapoarte de trafic, rapoarte meteorologice și așa mai departe. Acum aveți o bibliotecă pentru generarea de scuze pentru studenții universitari, dar are o interfață diferită: StudentExcuse
.
public interface StudentExcuse {
String generateExcuse();
void dislikeExcuse(String excuse);
}
Această interfață are două metode: generateExcuse
, care generează o scuză și dislikeExcuse
, care împiedică apariția scuzei din nou în viitor. Biblioteca terță parte nu poate fi editată, adică nu îi puteți modifica codul sursă. Ceea ce avem acum este un sistem cu două clase care implementează interfața Excuse
și o bibliotecă cu o SuperStudentExcuse
clasă care implementează StudentExcuse
interfața:
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
}
}
Codul nu poate fi schimbat. Ierarhia de clasă curentă arată astfel: 

Excuse
interfața. Dar ierarhia suplimentară este nedorită în aplicațiile serioase: introducerea unui element rădăcină comun rupe arhitectura. Ar trebui să implementați o clasă intermediară care ne va permite să folosim atât funcționalitatea nouă, cât și cea veche cu pierderi minime. Pe scurt, ai nevoie de un adaptor .
Principiul din spatele modelului adaptorului
Un adaptor este un obiect intermediar care permite apelurilor de metodă ale unui obiect să fie înțelese de către altul. Să implementăm un adaptor pentru exemplul nostru și să îl numimMiddleware
. Adaptorul nostru trebuie să implementeze o interfață compatibilă cu unul dintre obiecte. Lasă să fie Excuse
. Aceasta permite Middleware
apelarea metodelor primului obiect. Middleware
primește apeluri și le redirecționează într-un mod compatibil către al doilea obiect. Iată implementarea Middleware
cu metodele generateExcuse
și dislikeExcuse
:
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
}
Testare (în codul client):
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
}
}
Ieșire:
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:
O scuză incredibilă adaptată condițiilor meteo actuale, blocajelor de trafic sau întârzierilor în orarul transportului public. Metoda generateExcuse
trece pur și simplu apelul către alt obiect, fără modificări suplimentare. Metoda dislikeExcuse
ne-a impus să punem mai întâi pe lista neagră scuza. Capacitatea de a efectua procesare intermediară a datelor este un motiv pentru care oamenii iubesc modelul adaptorului. Dar cum rămâne cu likeExcuse
metoda, care face parte din Excuse
interfață, dar nu face parte din StudentExcuse
interfață? Noua funcționalitate nu acceptă această operațiune. A UnsupportedOperationException
fost inventat pentru această situație. Este aruncat dacă operația solicitată nu este suportată. Să-l folosim. Iată cum Middleware
arată noua implementare a clasei:
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.
}
}
La prima vedere, această soluție nu pare foarte bună, dar imitarea funcționalității poate complica situația. Dacă clientul acordă atenție, iar adaptorul este bine documentat, o astfel de soluție este acceptabilă.
Când să folosiți un adaptor
-
Când trebuie să utilizați o clasă terță parte, dar interfața sa este incompatibilă cu aplicația principală. Exemplul de mai sus arată cum să creați un obiect adaptor care include apeluri într-un format pe care un obiect țintă îl poate înțelege.
-
Când mai multe subclase existente au nevoie de unele funcționalități comune. În loc să creați subclase suplimentare (care va duce la duplicarea codului), este mai bine să utilizați un adaptor.
Avantaje și dezavantaje
Avantaj: Adaptorul ascunde de la client detaliile procesării cererilor de la un obiect la altul. Codul clientului nu se gândește la formatarea datelor sau la gestionarea apelurilor către metoda țintă. Este prea complicat, iar programatorii sunt leneși :) Dezavantaj: baza de cod a proiectului este complicată de clase suplimentare. Dacă aveți o mulțime de interfețe incompatibile, numărul de clase suplimentare poate deveni de negestionat.Nu confundați un adaptor cu o fațadă sau un decorator
Doar cu o inspecție superficială, un adaptor ar putea fi confundat cu modelele de fațadă și decor. Diferența dintre un adaptor și o fațadă este că o fațadă introduce o nouă interfață și înfășoară întregul subsistem. Și un decorator, spre deosebire de un adaptor, schimbă obiectul în sine mai degrabă decât interfața.Algoritm pas cu pas
-
În primul rând, asigurați-vă că aveți o problemă pe care acest model o poate rezolva.
-
Definiți interfața client care va fi utilizată pentru a interacționa indirect cu obiecte incompatibile.
-
Faceți ca clasa adaptorului să moștenească interfața definită în pasul anterior.
-
În clasa adaptorului, creați un câmp pentru a stoca o referință la obiectul adaptat. Această referință este transmisă constructorului.
-
Implementați toate metodele de interfață client în adaptor. O metodă poate:
-
Transmite apelurile fără a face modificări
-
Modificați sau completați datele, măriți/scădeți numărul de apeluri către metoda țintă etc.
-
În cazuri extreme, dacă o anumită metodă rămâne incompatibilă, aruncați o excepție UnsupportedOperationException. Operațiunile nesuportate trebuie să fie strict documentate.
-
-
Dacă aplicația folosește doar clasa adaptorului prin interfața client (ca în exemplul de mai sus), atunci adaptorul poate fi extins fără durere în viitor.
GO TO FULL VERSION