CodeGym /จาวาบล็อก /สุ่ม /รูปแบบการออกแบบอะแดปเตอร์
John Squirrels
ระดับ
San Francisco

รูปแบบการออกแบบอะแดปเตอร์

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะพูดถึงหัวข้อใหม่ที่สำคัญ: รูปแบบการออกแบบ . รูปแบบเหล่านี้คืออะไร? ฉันคิดว่าคุณต้องรู้จักสำนวน " don't reinvent the wheel " ในการเขียนโปรแกรม เช่นเดียวกับในพื้นที่อื่นๆ มีสถานการณ์ทั่วไปจำนวนมาก เมื่อการพัฒนาซอฟต์แวร์พัฒนาขึ้น โซลูชันสำเร็จรูปที่ใช้งานได้จึงถูกสร้างขึ้นสำหรับแต่ละโซลูชัน โซลูชันเหล่านี้เรียกว่ารูปแบบการออกแบบ ตามแบบแผน รูปแบบคือวิธีแก้ปัญหาบางอย่างที่มีสูตรดังนี้: "ถ้าคุณต้องการทำ X ในโปรแกรมของคุณ นี่เป็นวิธีที่ดีที่สุดที่จะทำ" มีรูปแบบมากมาย หนังสือที่ยอดเยี่ยม "Head First Design Patterns" ซึ่งคุณควรทำความคุ้นเคยอย่างแน่นอนนั้นอุทิศให้กับพวกเขา รูปแบบการออกแบบอแดปเตอร์ - 2กล่าวโดยสังเขป รูปแบบประกอบด้วยปัญหาทั่วไปและวิธีแก้ปัญหาที่สอดคล้องกันซึ่งถือได้ว่าเป็นมาตรฐานประเภทหนึ่ง ในบทเรียนวันนี้ เราจะพบกับรูปแบบใดรูปแบบหนึ่งต่อไปนี้: Adapter ชื่อก็บอกอยู่แล้ว และคุณเคยเจออแดปเตอร์หลายครั้งในชีวิตจริง อะแดปเตอร์ทั่วไปบางส่วนคือตัวอ่านการ์ดที่คอมพิวเตอร์และแล็ปท็อปจำนวนมากมี รูปแบบการออกแบบอแดปเตอร์ - 3สมมติว่าเรามีการ์ดหน่วยความจำบางประเภท แล้วปัญหาคืออะไร? มันไม่รู้วิธีการโต้ตอบกับคอมพิวเตอร์ พวกเขาไม่ได้ใช้อินเทอร์เฟซร่วมกัน คอมพิวเตอร์มีพอร์ต USB แต่เราไม่สามารถใส่การ์ดหน่วยความจำเข้าไปได้ ไม่สามารถเสียบการ์ดเข้ากับคอมพิวเตอร์ได้ เราจึงไม่สามารถบันทึกรูปภาพ วิดีโอ และข้อมูลอื่นๆ ได้ เครื่องอ่านบัตรเป็นอะแดปเตอร์ที่ช่วยแก้ปัญหานี้ได้ ท้ายที่สุดก็มีสาย USB! ไม่เหมือนตัวการ์ดตรงที่เสียบการ์ดรีดเดอร์เข้ากับคอมพิวเตอร์ได้ พวกเขาใช้อินเทอร์เฟซร่วมกันกับคอมพิวเตอร์: USB มาดูกันว่าสิ่งนี้มีลักษณะอย่างไรในทางปฏิบัติ:

public interface USB { 

   void connectWithUsbCable(); 
}
นี่คืออินเทอร์เฟซ USB ของเราที่มีวิธีการเชื่อมต่อผ่าน 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!"); 
   } 
}
นี่คือคลาสของเราซึ่งเป็นตัวแทนของการ์ดหน่วยความจำ มันมี 2 วิธีที่เราต้องการอยู่แล้ว แต่นี่คือปัญหา: มันไม่ได้ใช้อินเตอร์เฟส USB ไม่สามารถเสียบการ์ดเข้ากับพอร์ต USB

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(); 
   } 
}
และนี่คืออะแดปเตอร์ของเรา! อะไรCardReaderclass do และสิ่งที่ทำให้มันเป็นอะแดปเตอร์? มันง่ายทั้งหมด คลาสที่กำลังปรับ (MemoryCard) กลายเป็นหนึ่งในฟิลด์ของอแด็ปเตอร์ สิ่งนี้สมเหตุสมผล เมื่อเราใส่การ์ดหน่วยความจำเข้าไปในการ์ดรีดเดอร์ในชีวิตจริง การ์ดนั้นก็กลายเป็นส่วนหนึ่งของการ์ดนั้นด้วย อะแด็ปเตอร์แชร์อินเทอร์เฟซกับคอมพิวเตอร์ไม่เหมือนกับการ์ดหน่วยความจำ มีสาย USB กล่าวคือสามารถเชื่อมต่อกับอุปกรณ์อื่นผ่าน USB นั่นเป็นเหตุผลที่คลาส CardReader ของเราใช้อินเทอร์เฟซ USB แต่เกิดอะไรขึ้นกันแน่ในวิธีนี้? สิ่งที่เราต้องเกิดขึ้น! อะแดปเตอร์มอบหมายงานให้กับการ์ดหน่วยความจำของเรา แท้จริงแล้วอะแดปเตอร์ไม่ได้ทำอะไรเลย เครื่องอ่านบัตรไม่มีฟังก์ชันการทำงานอิสระใดๆ หน้าที่ของมันมีเพียงเชื่อมต่อคอมพิวเตอร์และการ์ดหน่วยความจำเพื่อให้การ์ดทำงาน — คัดลอกไฟล์!connectWithUsbCable()วิธีการ) เพื่อตอบสนอง "ความต้องการ" ของการ์ดหน่วยความจำ มาสร้างโปรแกรมไคลเอนต์ที่จะจำลองบุคคลที่ต้องการคัดลอกข้อมูลจากการ์ดหน่วยความจำ:

public class Main { 

   public static void main(String[] args) { 

       USB cardReader = new CardReader(new MemoryCard()); 
       cardReader.connectWithUsbCable(); 
   } 
}
แล้วเราได้อะไร? เอาต์พุตคอนโซล:

Memory card successfully inserted! 
The data has been copied to the computer!
ยอดเยี่ยม. เราบรรลุวัตถุประสงค์แล้ว! นี่คือลิงก์ไปยังวิดีโอที่มีข้อมูลเกี่ยวกับรูปแบบ Adapter:

คลาสนามธรรมสำหรับผู้อ่านและนักเขียน

ตอนนี้เราจะกลับไปที่กิจกรรมโปรดของเรา: เรียนรู้เกี่ยวกับคลาสใหม่สองสามคลาสสำหรับการทำงานกับอินพุตและเอาต์พุต :) ฉันสงสัยว่าเราได้เรียนรู้ไปกี่คลาสแล้ว วันนี้เราจะพูดคุยเกี่ยวกับReader และWriterชั้นเรียน ทำไมต้องเป็นคลาสเหล่านั้นโดยเฉพาะ? เนื่องจากเกี่ยวข้องกับส่วนก่อนหน้าของเราเกี่ยวกับอะแดปเตอร์ ลองตรวจสอบรายละเอียดเพิ่มเติม เราจะเริ่มต้น  Readerด้วย Readerเป็นคลาสนามธรรม ดังนั้นเราจะไม่สามารถสร้างออบเจกต์ได้อย่างชัดเจน   แต่จริงๆแล้วคุณคุ้นเคยกับมันแล้ว! ท้ายที่สุดคุณคุ้นเคยกับคลาสBufferedReaderและInputStreamReaderคลาสซึ่งเป็นลูกหลานของมันเป็นอย่างดี :)

public class BufferedReader extends Reader { 
… 
} 

public class InputStreamReader extends Reader { 
… 
}
คลาสInputStreamReaderเป็นอะแดปเตอร์แบบคลาสสิก อย่างที่คุณคงจำได้ เราสามารถส่งInputStreamอ็อบเจกต์ไปยังคอนสตรัคเตอร์ของมันได้ ในการทำเช่นนี้ เรามักจะใช้System.inตัวแปร:

public static void main(String[] args) { 

   InputStreamReader inputStreamReader = new InputStreamReader(System.in); 
}
แต่จะInputStreamReaderทำอย่างไร? เช่นเดียวกับอแด็ปเตอร์ทุกตัว มันแปลงอินเทอร์เฟซหนึ่งเป็นอีกอินเทอร์เฟซหนึ่ง  ในกรณีนี้คือInputStreamส่วนต่อประสานกับReaderส่วนต่อประสาน เริ่มแรกเรามีInputStreamคลาส ใช้งานได้ดี แต่คุณสามารถใช้เพื่ออ่านแต่ละไบต์เท่านั้น นอกจากนี้เรายังมีReaderคลาสนามธรรม มีฟังก์ชันที่มีประโยชน์มาก — รู้วิธีอ่านอักขระ! เราต้องการความสามารถนี้อย่างแน่นอน แต่ที่นี่เราประสบปัญหาคลาสสิกที่มักจะแก้ไขโดยอะแดปเตอร์ — อินเทอร์เฟซที่เข้ากันไม่ได้ นั่นหมายความว่าอย่างไร? มาดูเอกสาร Oracle กัน นี่คือวิธีการของInputStreamชั้นเรียน รูปแบบการออกแบบอแดปเตอร์ - 4ชุดของวิธีการคือสิ่งที่อินเทอร์เฟซคืออะไร อย่างที่คุณเห็นคลาสนี้มีread()วิธีการ (ในความเป็นจริงมีไม่กี่รูปแบบ) แต่สามารถอ่านได้เฉพาะไบต์: ทีละไบต์หรือหลายไบต์โดยใช้บัฟเฟอร์ แต่ตัวเลือกนี้ไม่เหมาะกับเรา — เราต้องการอ่านอักขระ เราต้องการฟังก์ชันที่ ใช้ งานแล้วในReaderคลาสนามธรรม เรายังสามารถดูสิ่งนี้ได้ในเอกสารประกอบ รูปแบบการออกแบบอะแดปเตอร์ - 5อย่างไรก็ตาม อินเทอร์เฟซ InputStreamและ  Readerเข้ากันไม่ได้! อย่างที่คุณเห็น การใช้งานเมธอดทุกread()ครั้งมีพารามิเตอร์และค่าส่งคืนที่แตกต่างกัน และนี่คือที่เราต้องการInputStreamReader! มันจะทำหน้าที่เป็นตัวแปลงระหว่างชั้นเรียนของเรา ในตัวอย่างกับเครื่องอ่านบัตรซึ่งเราพิจารณาข้างต้น เราใส่อินสแตนซ์ของคลาสที่กำลังดัดแปลง "ภายใน" คลาสของอะแดปเตอร์ กล่าวคือ เราส่งต่อหนึ่งไปยังตัวสร้าง ในตัวอย่างก่อนหน้านี้ เราใส่MemoryCardวัตถุไว้ข้างCardReaderใน ตอนนี้เรากำลังส่งInputStream วัตถุไปยังInputStreamReaderตัวสร้าง! เราใช้System.inตัวแปรที่เราคุ้นเคยเป็นInputStream:

public static void main(String[] args) { 

   InputStreamReader inputStreamReader = new InputStreamReader(System.in); 
}
และแน่นอน เมื่อดูที่เอกสารประกอบของInputStreamReaderเราจะเห็นว่าการปรับแต่งสำเร็จแล้ว :) ตอนนี้เรามีวิธีอ่านอักขระในการกำจัดของเราแล้ว รูปแบบการออกแบบอะแดปเตอร์ - 6และแม้ว่าSystem.inออบเจ็กต์ของเรา (สตรีมที่เชื่อมโยงกับแป้นพิมพ์) จะไม่อนุญาตให้ทำเช่นนี้ แต่ผู้สร้างภาษาได้แก้ปัญหานี้โดยการใช้รูปแบบอะแดปเตอร์ คลาสReaderนามธรรม เช่นเดียวกับคลาส I/O ส่วนใหญ่ มีพี่ชายฝาแฝด  Writer— มีข้อได้เปรียบใหญ่เช่นเดียวกับ  Reader — มีอินเทอร์เฟซที่สะดวกสำหรับการทำงานกับตัวละคร ด้วยเอาต์พุตสตรีม ปัญหาและแนวทางแก้ไขจะมีลักษณะเหมือนกันกับสตรีมอินพุต มีOutputStreamคลาสที่เขียนได้เฉพาะไบต์เท่านั้น มีWriterคลาสนามธรรมที่รู้วิธีทำงานกับอักขระ และมีสองอินเทอร์เฟซที่เข้ากันไม่ได้ ปัญหานี้ได้รับการแก้ไขอีกครั้งโดยรูปแบบอะแดปเตอร์ เราใช้OutputStreamWriterคลาสเพื่อปรับอินเทอร์เฟซทั้งสองของคลาสWriter และ  OutputStream คลาสให้เข้ากันได้ อย่างง่ายดาย หลังจากส่งOutputStreamกระแสไบต์ไปยังตัวสร้าง เราสามารถใช้ an OutputStreamWriterเพื่อเขียนอักขระแทนไบต์ได้!

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();
   } 
}
เราเขียนอักขระด้วยรหัส 32144 (綐) ลงในไฟล์ของเรา ทำให้ไม่ต้องทำงานกับไบต์ :) แค่นั้นแหละสำหรับวันนี้ แล้วพบกันในบทเรียนต่อไป! :)
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION