
Meer over het probleem
Eerst simuleren we het gedrag van het oude systeem. Stel dat het excuses genereert om te laat op het werk of op school te komen. Om dit te doen, heeft het eenExcuse
interface met generateExcuse()
, likeExcuse()
en dislikeExcuse()
methoden.
public interface Excuse {
String generateExcuse();
void likeExcuse(String excuse);
void dislikeExcuse(String excuse);
}
De WorkExcuse
klasse implementeert deze interface:
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
}
}
Laten we ons voorbeeld testen:
Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
Uitgang:
"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.
Stel je nu voor dat je een dienst voor het genereren van excuses hebt gelanceerd, statistieken hebt verzameld en merkt dat de meeste van je gebruikers universiteitsstudenten zijn. Om deze groep beter van dienst te zijn, heb je een andere ontwikkelaar gevraagd een systeem te maken dat excuses genereert, specifiek voor universiteitsstudenten. Het ontwikkelingsteam voerde marktonderzoek uit, rangschikte excuses, koppelde wat kunstmatige intelligentie aan en integreerde de dienst met verkeersrapporten, weerberichten, enzovoort. Nu heb je een bibliotheek voor het genereren van excuses voor universiteitsstudenten, maar deze heeft een andere interface: StudentExcuse
.
public interface StudentExcuse {
String generateExcuse();
void dislikeExcuse(String excuse);
}
Deze interface heeft twee methoden: generateExcuse
, waarmee een excuus wordt gegenereerd, en dislikeExcuse
, waarmee wordt voorkomen dat het excuus in de toekomst opnieuw verschijnt. De bibliotheek van derden kan niet worden bewerkt, dwz u kunt de broncode niet wijzigen. Wat we nu hebben is een systeem met twee klassen die de Excuse
interface implementeren, en een bibliotheek met een klasse die de interface SuperStudentExcuse
implementeert :StudentExcuse
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
}
}
De code kan niet worden gewijzigd. De huidige klassenhiërarchie ziet er als volgt uit: 

Excuse
interface hernoemen. Maar de extra hiërarchie is ongewenst bij serieuze toepassingen: het introduceren van een gemeenschappelijk root-element breekt de architectuur. U moet een tussenklasse implementeren waarmee we zowel de nieuwe als de oude functionaliteit kunnen gebruiken met minimale verliezen. Kortom, je hebt een adapter nodig .
Het principe achter het adapterpatroon
Een adapter is een tussenliggend object waarmee de methodeaanroepen van het ene object door een ander kunnen worden begrepen. Laten we een adapter voor ons voorbeeld implementeren en deze noemenMiddleware
. Onze adapter moet een interface implementeren die compatibel is met een van de objecten. Laat het zo zijn Excuse
. Dit maakt het mogelijk Middleware
om de methoden van het eerste object aan te roepen. Middleware
ontvangt oproepen en stuurt ze op een compatibele manier door naar het tweede object. Dit is de Middleware
implementatie met de methoden generateExcuse
en 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
}
Testen (in klantcode):
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
}
}
Uitgang:
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:
Een ongelooflijk excuus aangepast aan de huidige weersomstandigheden, files of vertragingen in de dienstregeling van het openbaar vervoer. De generateExcuse
methode geeft de aanroep gewoon door aan een ander object, zonder aanvullende wijzigingen. De dislikeExcuse
methode vereiste dat we eerst het excuus op de zwarte lijst zetten. De mogelijkheid om tussentijdse gegevensverwerking uit te voeren is een reden waarom mensen dol zijn op het adapterpatroon. Maar hoe zit het met de likeExcuse
methode, die deel uitmaakt van de Excuse
interface maar geen deel uitmaakt van de StudentExcuse
interface? De nieuwe functionaliteit ondersteunt deze bewerking niet. De UnsupportedOperationException
is uitgevonden voor deze situatie. Het wordt gegenereerd als de gevraagde bewerking niet wordt ondersteund. Laten we het gebruiken. Zo Middleware
ziet de nieuwe implementatie van de klas eruit:
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.
}
}
Op het eerste gezicht lijkt deze oplossing niet erg goed, maar het imiteren van de functionaliteit kan de situatie bemoeilijken. Als de klant oplet en de adapter goed gedocumenteerd is, is een dergelijke oplossing acceptabel.
Wanneer een adapter gebruiken?
-
Wanneer u een klasse van derden moet gebruiken, maar de interface niet compatibel is met de hoofdtoepassing. Het bovenstaande voorbeeld laat zien hoe u een adapterobject maakt dat oproepen verpakt in een formaat dat een doelobject kan begrijpen.
-
Wanneer verschillende bestaande subklassen een gemeenschappelijke functionaliteit nodig hebben. In plaats van extra subklassen te maken (wat zal leiden tot duplicatie van code), is het beter om een adapter te gebruiken.
Voor-en nadelen
Voordeel: de adapter verbergt voor de client de details van de verwerkingsverzoeken van het ene object naar het andere. De clientcode denkt niet na over het formatteren van gegevens of het afhandelen van aanroepen naar de doelmethode. Het is te gecompliceerd en programmeurs zijn lui :) Nadeel: de codebasis van het project wordt gecompliceerd door extra klassen. Als u veel incompatibele interfaces heeft, kan het aantal extra klassen onhandelbaar worden.Verwar een adapter niet met een gevel of decorateur
Met slechts een oppervlakkige inspectie zou een adapter kunnen worden verward met de gevel- en decoratiepatronen. Het verschil tussen een adapter en een gevel is dat een gevel een nieuwe interface introduceert en het hele subsysteem omhult. En een decorateur verandert, in tegenstelling tot een adapter, het object zelf in plaats van de interface.Stap voor stap algoritme
-
Zorg er eerst voor dat je een probleem hebt dat dit patroon kan oplossen.
-
Definieer de clientinterface die zal worden gebruikt voor indirecte interactie met incompatibele objecten.
-
Laat de adapterklasse de interface overnemen die in de vorige stap is gedefinieerd.
-
Maak in de adapterklasse een veld om een verwijzing naar het adaptee-object op te slaan. Deze verwijzing wordt doorgegeven aan de constructor.
-
Implementeer alle clientinterfacemethoden in de adapter. Een methode kan:
-
Gesprekken doorgeven zonder wijzigingen aan te brengen
-
Wijzig of vul gegevens aan, verhoog/verlaag het aantal oproepen naar de doelmethode, etc.
-
In extreme gevallen, als een bepaalde methode onverenigbaar blijft, genereert u een UnsupportedOperationException. Niet-ondersteunde bewerkingen moeten strikt worden gedocumenteerd.
-
-
Als de applicatie alleen de adapterklasse gebruikt via de clientinterface (zoals in het bovenstaande voorbeeld), kan de adapter in de toekomst probleemloos worden uitgebreid.
GO TO FULL VERSION