CodeGym /Java Blog /Willekeurig /Adapter ontwerppatroon
John Squirrels
Niveau 41
San Francisco

Adapter ontwerppatroon

Gepubliceerd in de groep Willekeurig
Hoi! Vandaag gaan we in op een belangrijk nieuw onderwerp: ontwerppatronen . Wat zijn deze patronen? Ik denk dat je de uitdrukking " niet opnieuw het wiel uitvinden " moet kennen. Bij programmeren zijn er, net als op veel andere gebieden, een groot aantal veelvoorkomende situaties. Naarmate de softwareontwikkeling evolueerde, zijn er voor elk van hen kant-en-klare oplossingen gemaakt die werken. Deze oplossingen worden ontwerppatronen genoemd. Volgens afspraak is een patroon een oplossing die als volgt is geformuleerd: "als je X in je programma moet doen, dan is dit de beste manier om het te doen". Er zijn veel patronen. Het uitstekende boek "Head First Design Patterns", waar u zeker mee vertrouwd moet raken, is aan hen opgedragen. Ontwerppatroon adapter - 2Kort gezegd bestaat een patroon uit een gemeenschappelijk probleem en een bijbehorende oplossing die als een soort norm kan worden beschouwd. In de les van vandaag zullen we een van deze patronen tegenkomen: Adapter. De naam zegt het al, en je bent in het echte leven al vaak adapters tegengekomen. Enkele van de meest voorkomende adapters zijn de kaartlezers die veel computers en laptops hebben. Ontwerppatroon adapter - 3Stel dat we een soort geheugenkaart hebben. Wat is het probleem? Het weet niet hoe het met de computer moet communiceren. Ze delen geen gemeenschappelijke interface. De computer heeft een USB-poort, maar we kunnen de geheugenkaart er niet in steken. De kaart kan niet in de computer worden gestoken, dus we kunnen onze foto's, video's en andere gegevens niet opslaan. Een kaartlezer is een adapter die dit probleem oplost. Er zit tenslotte een USB-kabel op! In tegenstelling tot de kaart zelf kan de kaartlezer op de computer worden aangesloten. Ze delen een gemeenschappelijke interface met de computer: USB. Laten we eens kijken hoe dit er in de praktijk uitziet:

public interface USB { 

   void connectWithUsbCable(); 
}
Dit is onze USB-interface met slechts één methode om verbinding te maken via USB.

public class MemoryCard { 

   public void insert() { 
       System.out.println("Memory card successfully inserted!"); 
   } 

   public void copyData() { 
       System.out.println("The data has been copied to the computer!"); 
   } 
}
Dit is onze klas die de geheugenkaart vertegenwoordigt. Het heeft al de 2 methoden die we nodig hebben, maar hier is het probleem: het implementeert de USB-interface niet. De kaart kan niet in de USB-poort worden gestoken.

public class CardReader implements USB { 

   private MemoryCard memoryCard; 

   public CardReader(MemoryCard memoryCard) { 
       this.memoryCard = memoryCard; 
   } 

   @Override 
   public void connectWithUsbCable() { 
       this.memoryCard.insert(); 
       this.memoryCard.copyData(); 
   } 
}
En hier is onze adapter! Wat doet deCardReaderclass do en wat maakt het precies tot een adapter? Het is allemaal eenvoudig. De klasse die wordt aangepast (MemoryCard) wordt een van de velden van de adapter. Dit slaat ergens op. Wanneer we in het echte leven een geheugenkaart in een kaartlezer stoppen, wordt deze er ook een onderdeel van. In tegenstelling tot de geheugenkaart deelt de adapter een interface met de computer. Het heeft een USB-kabel, dwz het kan via USB op andere apparaten worden aangesloten. Daarom implementeert onze CardReader-klasse de USB-interface. Maar wat gebeurt er precies binnen deze methode? Precies wat er moet gebeuren! De adapter delegeert het werk naar onze geheugenkaart. De adapter doet namelijk zelf niets. Een kaartlezer heeft geen zelfstandige functionaliteit. Zijn taak is alleen om de computer en de geheugenkaart met elkaar te verbinden, zodat de kaart zijn werk kan doen: bestanden kopiëren!connectWithUsbCable()methode) om aan de "behoeften" van de geheugenkaart te voldoen. Laten we een clientprogramma maken dat een persoon simuleert die gegevens van een geheugenkaart wil kopiëren:

public class Main { 

   public static void main(String[] args) { 

       USB cardReader = new CardReader(new MemoryCard()); 
       cardReader.connectWithUsbCable(); 
   } 
}
Dus wat hebben we gekregen? Console-uitvoer:

Memory card successfully inserted! 
The data has been copied to the computer!
Uitstekend. We hebben ons doel bereikt! Hier is een link naar een video met informatie over het adapterpatroon:

Reader en Writer abstracte lessen

Nu keren we terug naar onze favoriete bezigheid: leren over een paar nieuwe klassen voor het werken met invoer en uitvoer :) Ik vraag me af hoeveel we er al hebben geleerd. Vandaag gaan we het hebben over de lessen Reader en . WriterWaarom specifiek die lessen? Omdat ze verband houden met ons vorige gedeelte over adapters. Laten we ze in meer detail bekijken. We beginnen met  Reader. Readeris een abstracte klasse, dus we kunnen geen objecten expliciet maken.   Maar eigenlijk ben je er al bekend mee! Je bent tenslotte goed bekend met de klassen BufferedReaderen InputStreamReader, die zijn afstammelingen zijn :)

public class BufferedReader extends Reader { 
… 
} 

public class InputStreamReader extends Reader { 
… 
}
De InputStreamReaderklasse is een klassieke adapter. Zoals u zich waarschijnlijk herinnert, kunnen we een InputStreamobject doorgeven aan de constructor. Hiervoor gebruiken we meestal de System.invariabele:

public static void main(String[] args) { 

   InputStreamReader inputStreamReader = new InputStreamReader(System.in); 
}
Maar wat doet InputStreamReaderhet? Zoals elke adapter converteert het de ene interface naar de andere.  In dit geval de InputStreaminterface naar de Readerinterface. In eerste instantie hebben we de InputStreamklas. Het werkt goed, maar je kunt het alleen gebruiken om individuele bytes te lezen. Daarnaast hebben we een Readerabstracte les. Het heeft een aantal zeer nuttige functionaliteiten - het kan karakters lezen! We hebben dit vermogen zeker nodig. Maar hier hebben we te maken met het klassieke probleem dat meestal wordt opgelost door adapters: incompatibele interfaces. Wat betekent dat? Laten we eens kijken naar de Oracle-documentatie. Hier zijn de methoden van de InputStreamklas. Ontwerppatroon adapter - 4Een set methoden is precies wat een interface is. Zoals je kunt zien, heeft deze klasse eenread()methode (in feite een paar varianten), maar het kan alleen bytes lezen: individuele bytes of meerdere bytes met behulp van een buffer. Maar deze optie past niet bij ons - we willen karakters lezen. We hebben de functionaliteit nodig die al in de abstracte klasse is geïmplementeerdReader . Dit zien we ook terug in de documentatie. De interfaces en  zijn Ontwerppatroon adapter - 5echter niet compatibel! Zoals u kunt zien, heeft elke implementatie van de methode verschillende parameters en retourwaarden. En dit is waar we het nodig hebben ! Het zal fungeren als een adapter tussen onze klassen. InputStreamReaderread()InputStreamReaderNet als in het voorbeeld met de kaartlezer, dat we hierboven hebben besproken, plaatsen we een instantie van de klasse die wordt aangepast "binnen" de adapterklasse, dwz we geven er een door aan de constructor. In het vorige voorbeeld hebben we een MemoryCardobject erin geplaatst CardReader. Nu geven we een InputStream object door aan de InputStreamReaderconstructor! We gebruiken onze bekende System.invariabele als de InputStream:

public static void main(String[] args) { 

   InputStreamReader inputStreamReader = new InputStreamReader(System.in); 
}
En inderdaad, als we naar de documentatie voor kijken InputStreamReader, kunnen we zien dat de aanpassing is gelukt :) Nu hebben we methoden om karakters te lezen tot onze beschikking. Ontwerppatroon adapter - 6En hoewel ons System.inobject (de stream gebonden aan het toetsenbord) dit aanvankelijk niet toestond, hebben de makers van de taal dit probleem opgelost door het adapterpatroon te implementeren. De Readerabstracte klasse heeft, zoals de meeste I/O-klassen, een tweelingbroer —  Writer. Het heeft hetzelfde grote voordeel als  Reader : het biedt een handige interface voor het werken met karakters. Bij outputstromen zien het probleem en de oplossing er hetzelfde uit als bij inputstromen. Er is een OutputStreamklasse die alleen bytes kan schrijven, er is eenWriterabstracte klasse die weet hoe ze met karakters moet werken, en er zijn twee incompatibele interfaces. Dit probleem wordt wederom opgelost door het adapterpatroon. We gebruiken de OutputStreamWriterklasse om de twee interfaces van de klassen Writer en  gemakkelijk OutputStream aan elkaar aan te passen. Nadat we een OutputStreambytestroom aan de constructor hebben doorgegeven, kunnen we een gebruiken OutputStreamWriterom tekens te schrijven in plaats van bytes!

import java.io.*; 

public class Main { 

   public static void main(String[] args) throws IOException { 

       OutputStreamWriter streamWriter = new OutputStreamWriter(new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt")); 
       streamWriter.write(32144); 
       streamWriter.close();
   } 
}
We hebben het personage met code 32144 (綐) naar ons bestand geschreven, waardoor we niet meer met bytes hoeven te werken :) Dat was het voor vandaag. Tot ziens in de volgende lessen! :)
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION