สวัสดี! วันนี้เราจะพูดถึงหัวข้อใหม่ที่สำคัญ: รูปแบบการออกแบบ . รูปแบบเหล่านี้คืออะไร? ฉันคิดว่าคุณต้องรู้จักสำนวน " don't reinvent the wheel " ในการเขียนโปรแกรม เช่นเดียวกับในพื้นที่อื่นๆ มีสถานการณ์ทั่วไปจำนวนมาก เมื่อการพัฒนาซอฟต์แวร์พัฒนาขึ้น โซลูชันสำเร็จรูปที่ใช้งานได้จึงถูกสร้างขึ้นสำหรับแต่ละโซลูชัน โซลูชันเหล่านี้เรียกว่ารูปแบบการออกแบบ ตามแบบแผน รูปแบบคือวิธีแก้ปัญหาบางอย่างที่มีสูตรดังนี้: "ถ้าคุณต้องการทำ X ในโปรแกรมของคุณ นี่เป็นวิธีที่ดีที่สุดที่จะทำ" มีรูปแบบมากมาย หนังสือที่ยอดเยี่ยม "Head First Design Patterns" ซึ่งคุณควรทำความคุ้นเคยอย่างแน่นอนนั้นอุทิศให้กับพวกเขา กล่าวโดยสังเขป รูปแบบประกอบด้วยปัญหาทั่วไปและวิธีแก้ปัญหาที่สอดคล้องกันซึ่งถือได้ว่าเป็นมาตรฐานประเภทหนึ่ง ในบทเรียนวันนี้ เราจะพบกับรูปแบบใดรูปแบบหนึ่งต่อไปนี้: Adapter ชื่อก็บอกอยู่แล้ว และคุณเคยเจออแดปเตอร์หลายครั้งในชีวิตจริง อะแดปเตอร์ทั่วไปบางส่วนคือตัวอ่านการ์ดที่คอมพิวเตอร์และแล็ปท็อปจำนวนมากมี สมมติว่าเรามีการ์ดหน่วยความจำบางประเภท แล้วปัญหาคืออะไร? มันไม่รู้วิธีการโต้ตอบกับคอมพิวเตอร์ พวกเขาไม่ได้ใช้อินเทอร์เฟซร่วมกัน คอมพิวเตอร์มีพอร์ต 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();
}
}
และนี่คืออะแดปเตอร์ของเรา! อะไรCardReader
class 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
ชั้นเรียน ชุดของวิธีการคือสิ่งที่อินเทอร์เฟซคืออะไร อย่างที่คุณเห็นคลาสนี้มีread()
วิธีการ (ในความเป็นจริงมีไม่กี่รูปแบบ) แต่สามารถอ่านได้เฉพาะไบต์: ทีละไบต์หรือหลายไบต์โดยใช้บัฟเฟอร์ แต่ตัวเลือกนี้ไม่เหมาะกับเรา — เราต้องการอ่านอักขระ เราต้องการฟังก์ชันที่ ใช้ งานแล้วในReader
คลาสนามธรรม เรายังสามารถดูสิ่งนี้ได้ในเอกสารประกอบ อย่างไรก็ตาม อินเทอร์เฟซ InputStream
และ Reader
เข้ากันไม่ได้! อย่างที่คุณเห็น การใช้งานเมธอดทุกread()
ครั้งมีพารามิเตอร์และค่าส่งคืนที่แตกต่างกัน และนี่คือที่เราต้องการInputStreamReader
! มันจะทำหน้าที่เป็นตัวแปลงระหว่างชั้นเรียนของเรา ในตัวอย่างกับเครื่องอ่านบัตรซึ่งเราพิจารณาข้างต้น เราใส่อินสแตนซ์ของคลาสที่กำลังดัดแปลง "ภายใน" คลาสของอะแดปเตอร์ กล่าวคือ เราส่งต่อหนึ่งไปยังตัวสร้าง ในตัวอย่างก่อนหน้านี้ เราใส่MemoryCard
วัตถุไว้ข้างCardReader
ใน ตอนนี้เรากำลังส่งInputStream
วัตถุไปยังInputStreamReader
ตัวสร้าง! เราใช้System.in
ตัวแปรที่เราคุ้นเคยเป็นInputStream
:
public static void main(String[] args) {
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
และแน่นอน เมื่อดูที่เอกสารประกอบของInputStreamReader
เราจะเห็นว่าการปรับแต่งสำเร็จแล้ว :) ตอนนี้เรามีวิธีอ่านอักขระในการกำจัดของเราแล้ว และแม้ว่า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 (綐) ลงในไฟล์ของเรา ทำให้ไม่ต้องทำงานกับไบต์ :) แค่นั้นแหละสำหรับวันนี้ แล้วพบกันในบทเรียนต่อไป! :)
GO TO FULL VERSION