CodeGym /Java blogg /Slumpmässig /Adapter designmönster
John Squirrels
Nivå
San Francisco

Adapter designmönster

Publicerad i gruppen
Hej! Idag kommer vi att beröra ett viktigt nytt ämne: designmönster . Vilka är dessa mönster? Jag tror att du måste känna till uttrycket " uppfinn inte hjulet på nytt ". Inom programmering, liksom inom många andra områden, finns det ett stort antal vanliga situationer. I takt med att mjukvaruutvecklingen har utvecklats har färdiga lösningar som fungerar skapats för var och en av dem. Dessa lösningar kallas designmönster. Enligt konventionen är ett mönster någon lösning formulerad så här: "om du behöver göra X i ditt program, så är detta det bästa sättet att göra det". Det finns många mönster. Den utmärkta boken "Head First Design Patterns", som du definitivt borde bekanta dig med, är tillägnad dem. Adapterdesignmönster - 2Kortfattat består ett mönster av ett gemensamt problem och en motsvarande lösning som kan betraktas som en sorts standard. I dagens lektion kommer vi att möta ett av dessa mönster: Adapter. Namnet säger allt, och du har stött på adaptrar många gånger i verkligheten. Några av de vanligaste adaptrarna är kortläsarna som många datorer och bärbara datorer har. Adapterdesignmönster - 3Anta att vi har något slags minneskort. Så vad är problemet? Den vet inte hur man interagerar med datorn. De delar inte ett gemensamt gränssnitt. Datorn har en USB-port, men vi kan inte sätta in minneskortet i den. Kortet kan inte anslutas till datorn, så vi kan inte spara våra foton, videor och annan data. En kortläsare är en adapter som löser detta problem. Den har trots allt en USB-kabel! Till skillnad från själva kortet kan kortläsaren kopplas in i datorn. De delar ett gemensamt gränssnitt med datorn: USB. Låt oss se hur detta ser ut i praktiken:

public interface USB { 

   void connectWithUsbCable(); 
}
Detta är vårt USB-gränssnitt med bara en metod för att ansluta 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!"); 
   } 
}
Det här är vår klass som representerar minneskortet. Den har redan de två metoderna vi behöver, men här är problemet: Den implementerar inte USB-gränssnittet. Kortet kan inte sättas in i USB-porten.

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(); 
   } 
}
Och här är vår adapter! Vad görCardReaderklass gör och vad exakt gör det till en adapter? Det hela är enkelt. Klassen som anpassas (MemoryCard) blir ett av adapterns fält. Detta är vettigt. När vi stoppar in ett minneskort i en kortläsare i verkligheten blir det också en del av det. Till skillnad från minneskortet delar adaptern ett gränssnitt med datorn. Den har en USB-kabel, dvs den kan kopplas till andra enheter via USB. Det är därför vår CardReader-klass implementerar USB-gränssnittet. Men vad exakt händer i denna metod? Exakt vad vi behöver för att hända! Adaptern delegerar arbetet till vårt minneskort. Faktum är att adaptern inte gör någonting själv. En kortläsare har ingen oberoende funktion. Dess uppgift är bara att ansluta datorn och minneskortet för att kortet ska kunna göra sitt jobb — kopiera filer!connectWithUsbCable()metod) för att tillgodose minneskortets "behov". Låt oss skapa något klientprogram som kommer att simulera en person som vill kopiera data från ett minneskort:

public class Main { 

   public static void main(String[] args) { 

       USB cardReader = new CardReader(new MemoryCard()); 
       cardReader.connectWithUsbCable(); 
   } 
}
Så vad fick vi? Konsolutgång:

Memory card successfully inserted! 
The data has been copied to the computer!
Excellent. Vi uppnådde vårt mål! Här är en länk till en video med information om adaptermönstret:

Läsare och författare abstrakt klasser

Nu ska vi återgå till vår favoritaktivitet: lära oss om ett par nya klasser för att arbeta med input och output :) Jag undrar hur många vi redan har lärt oss om. Idag ska vi prata om Reader och Writerklasser. Varför just dessa klasser? Eftersom de är relaterade till vårt tidigare avsnitt om adaptrar. Låt oss undersöka dem mer i detalj. Vi börjar med  Reader. Readerär en abstrakt klass, så vi kommer inte att kunna skapa objekt explicit.   Men du är faktiskt redan bekant med det! Du är trots allt väl bekant med klasserna BufferedReaderoch InputStreamReader, som är dess ättlingar :)

public class BufferedReader extends Reader { 
… 
} 

public class InputStreamReader extends Reader { 
… 
}
Klassen InputStreamReaderär en klassisk adapter. Som du säkert minns kan vi skicka ett InputStreamobjekt till dess konstruktör. För att göra detta använder vi vanligtvis System.invariabeln:

public static void main(String[] args) { 

   InputStreamReader inputStreamReader = new InputStreamReader(System.in); 
}
Men vad InputStreamReadergör? Som varje adapter konverterar den ett gränssnitt till ett annat.  I det här fallet InputStreamgränssnittet till Readergränssnittet. Till en början har vi InputStreamklassen. Det fungerar bra, men du kan bara använda det för att läsa enskilda byte. Dessutom har vi en Readerabstrakt klass. Den har några mycket användbara funktioner - den vet hur man läser tecken! Vi behöver verkligen denna förmåga. Men här står vi inför det klassiska problemet som vanligtvis löses med adaptrar - inkompatibla gränssnitt. Vad betyder det? Låt oss ta en titt på Oracle-dokumentationen. Här är klassens metoder InputStream. Adapterdesignmönster - 4En uppsättning metoder är precis vad ett gränssnitt är. Som du kan se har denna klass enread()metod (faktiskt ett fåtal varianter), men den kan bara läsa byte: antingen enskilda byte eller flera byte med en buffert. Men det här alternativet passar oss inte – vi vill läsa karaktärer. Vi behöver den funktionalitet som redan är implementerad i Readerabstraktklassen . Det kan vi också se i dokumentationen. Adapterdesignmönster - 5Men gränssnitten InputStreamoch  Readerär inkompatibla! Som du kan se har varje implementering av read()metoden olika parametrar och returvärden. Och det är här vi behöver InputStreamReader! Den kommer att fungera som en adapter mellan våra klasser. Som i exemplet med kortläsaren, som vi övervägde ovan, lägger vi en instans av att klassen är anpassad "inuti" adapterklassen, dvs vi skickar en till dess konstruktor. I föregående exempel satte vi ett MemoryCardobjekt inuti CardReader. Nu skickar vi ett InputStream objekt till InputStreamReaderkonstruktören! Vi använder vår välbekanta System.invariabel som InputStream:

public static void main(String[] args) { 

   InputStreamReader inputStreamReader = new InputStreamReader(System.in); 
}
Och faktiskt, när vi tittar på dokumentationen för InputStreamReader, kan vi se att anpassningen lyckades :) Nu har vi metoder för att läsa karaktärer till vårt förfogande. Adapterdesignmönster - 6Och även om vårt System.inobjekt (strömmen bunden till tangentbordet) från början inte tillät detta, löste språkets skapare detta problem genom att implementera adaptermönstret. Den Readerabstrakta klassen, som de flesta I/O-klasser, har en tvillingbror —  Writer. Det har samma stora fördel som  Reader — det ger ett bekvämt gränssnitt för att arbeta med karaktärer. Med utströmmar ser problemet och dess lösning ut på samma sätt som med ingångsströmmar. Det finns en OutputStreamklass som bara kan skriva bytes, det finns enWriterabstrakt klass som vet hur man arbetar med karaktärer, och det finns två inkompatibla gränssnitt. Detta problem löses återigen av adaptermönstret. Vi använder OutputStreamWriterklassen för att enkelt anpassa de två gränssnitten för klasserna Writer och  OutputStream till varandra. Efter att ha skickat en OutputStreambyteström till konstruktorn kan vi använda en OutputStreamWriterför att skriva tecken snarare än byte!

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();
   } 
}
Vi skrev tecknet med koden 32144 (綐) till vår fil, vilket eliminerade behovet av att arbeta med bytes :) Det var allt för idag. Vi ses på nästa lektion! :)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION