JavaRush /Blog Java /Random-VI /Bộ điều hợp mẫu thiết kế

Bộ điều hợp mẫu thiết kế

Xuất bản trong nhóm
Xin chào! Hôm nay chúng ta sẽ đề cập đến một chủ đề mới quan trọng - các mẫu, hay nói cách khác - các mẫu thiết kế . Các mẫu là gì? Tôi nghĩ bạn biết câu nói “đừng phát minh lại bánh xe”. Trong lập trình, cũng như trong nhiều lĩnh vực khác, có rất nhiều tình huống điển hình. Đối với mỗi người trong số họ, trong quá trình phát triển lập trình, các giải pháp làm việc sẵn sàng đã được tạo ra. Đây là những mẫu thiết kế. Nói một cách tương đối, mẫu là một ví dụ nhất định đưa ra giải pháp cho một tình huống như: “nếu chương trình của bạn cần làm điều gì đó thì cách tốt nhất để làm điều đó”. Có rất nhiều mẫu, một cuốn sách xuất sắc “Nghiên cứu các mẫu thiết kế” được dành riêng cho chúng mà bạn chắc chắn nên đọc. Mẫu thiết kế “Adapter” - 2Nói một cách ngắn gọn nhất có thể, một mẫu bao gồm một vấn đề chung và giải pháp của nó, có thể được coi là một loại tiêu chuẩn. Trong bài giảng hôm nay chúng ta sẽ làm quen với một trong những mẫu này được gọi là “Bộ chuyển đổi”. Tên của nó đã nói lên điều đó và bạn đã nhiều lần bắt gặp các bộ điều hợp trong đời thực. Một trong những bộ điều hợp phổ biến nhất là đầu đọc thẻ, được trang bị trên nhiều máy tính và máy tính xách tay. Mẫu thiết kế “Adapter” - 3Hãy tưởng tượng rằng chúng ta có một loại thẻ nhớ. Vấn đề là gì? Thực tế là cô ấy không biết cách tương tác với máy tính. Họ không có giao diện chung. Máy tính có đầu nối USB nhưng bạn không thể cắm thẻ nhớ vào đó. Không thể lắp thẻ vào máy tính, do đó chúng ta sẽ không thể lưu ảnh, video và các dữ liệu khác của mình. Đầu đọc thẻ là một bộ chuyển đổi giải quyết được vấn đề này. Rốt cuộc, nó có cáp USB! Không giống như thẻ, đầu đọc thẻ có thể được lắp vào máy tính. Chúng có giao diện chung với máy tính - USB. Hãy xem nó trông như thế nào với một ví dụ:
public interface USB {

   void connectWithUsbCable();
}
Đây là giao diện USB của chúng tôi với phương pháp duy nhất là cắm cáp USB:
public class MemoryCard {

   public void insert() {
       System.out.println("Карта памяти успешно вставлена!");
   }

   public void copyData() {
       System.out.println("Данные скопированы на компьютер!");
   }
}
Đây là lớp của chúng tôi triển khai bản đồ bộ nhớ. Nó đã có 2 phương thức chúng ta cần, nhưng đây là vấn đề: nó không triển khai giao diện USB. Không thể cắm thẻ vào khe cắm 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();
   }
}
Và đây là bộ chuyển đổi của chúng tôi! Lớp họcCardReader làm gì và tại sao nó là một bộ chuyển đổi? Nó đơn giản. Lớp đang được điều chỉnh (bản đồ bộ nhớ) trở thành một trong các trường của bộ điều hợp. Điều này là hợp lý, vì trong đời thực, chúng ta cũng nhét một chiếc thẻ vào bên trong đầu đọc thẻ và nó cũng trở thành một phần của nó. Không giống như thẻ nhớ, bộ chuyển đổi có giao diện chung với máy tính. Nó có cáp USB, nghĩa là nó có thể kết nối với các thiết bị khác thông qua USB. Vì vậy, trong chương trình, lớp chúng ta CardReaderthực hiện giao diện USB. Nhưng điều gì xảy ra bên trong phương pháp này? Và ở đó xảy ra chính xác những gì chúng ta cần! Bộ điều hợp ủy quyền công việc cho thẻ nhớ của chúng tôi. Rốt cuộc, bản thân bộ chuyển đổi không làm gì cả, đầu đọc thẻ không có bất kỳ chức năng độc lập nào. Công việc của nó chỉ là liên kết máy tính với thẻ nhớ để thẻ có thể làm nhiệm vụ và copy file mà thôi! Bộ điều hợp của chúng tôi cho phép nó thực hiện điều này bằng cách cung cấp giao diện (phương thức connectWithUsbCable()) riêng cho “nhu cầu” của thẻ nhớ. Hãy tạo một số loại chương trình máy khách sẽ mô phỏng một người muốn sao chép dữ liệu từ thẻ nhớ:
public class Main {

   public static void main(String[] args) {

       USB cardReader = new CardReader(new MemoryCard());
       cardReader.connectWithUsbCable();

   }
}
Kết quả là chúng ta đã nhận được gì? Đầu ra của bảng điều khiển:
Карта памяти успешно вставлена!
Данные скопированы на компьютер!
Tuyệt vời, nhiệm vụ của chúng ta đã hoàn thành xuất sắc! Dưới đây là một số liên kết bổ sung có thông tin về mẫu Bộ điều hợp:

Các lớp trừu tượng Reader và Writer

Bây giờ chúng ta sẽ quay lại trò tiêu khiển yêu thích của mình: chúng ta sẽ học một vài lớp mới để làm việc với đầu vào và đầu ra :) Chúng ta đã học được bao nhiêu trong số đó rồi, tôi tự hỏi? Hôm nay chúng ta sẽ nói về các lớp ReaderWriter. Tại sao về họ? Bởi vì điều này sẽ liên quan đến phần trước của chúng ta - bộ điều hợp. Chúng ta hãy xem xét chúng chi tiết hơn. Hãy bắt đầu với Reader'a. Readerlà một lớp trừu tượng nên chúng ta không thể tạo các đối tượng của nó một cách rõ ràng. Nhưng trên thực tế, bạn đã biết anh ấy rồi! Suy cho cùng, những lớp bạn biết rõ BufferedReaderInputStreamReadernhững người thừa kế của nó :)
public class BufferedReader extends Reader {}

public class InputStreamReader extends Reader {}
Vì vậy, một lớp InputStreamReaderlà một bộ chuyển đổi cổ điển . Có lẽ bạn còn nhớ, chúng ta có thể truyền một đối tượng tới hàm tạo của nó InputStream. Thông thường chúng ta sử dụng một biến cho việc này System.in:
public static void main(String[] args) {

   InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
Nó làm gì InputStreamReader? Giống như bất kỳ bộ chuyển đổi nào, nó chuyển đổi giao diện này sang giao diện khác. Trong trường hợp này, giao diện InputStream'a đến giao diện Reader'a. Ban đầu chúng tôi có một lớp học InputStream. Nó hoạt động tốt nhưng chỉ có thể đọc từng byte riêng lẻ. Ngoài ra, chúng ta còn có một lớp trừu tượng Reader. Nó có chức năng tuyệt vời mà chúng tôi thực sự cần - nó có thể đọc ký tự! Tất nhiên, chúng tôi thực sự cần cơ hội này. Nhưng ở đây chúng ta phải đối mặt với một vấn đề kinh điển mà các bộ điều hợp thường giải quyết - giao diện không tương thích. Nó biểu hiện như thế nào? Hãy nhìn thẳng vào tài liệu của Oracle. Dưới đây là các phương pháp lớp InputStream. Паттерн проектирования «Адаптер» - 4Một tập hợp các phương thức là một giao diện. Như bạn có thể thấy, read()lớp này có một phương thức (thậm chí trong một số phiên bản), nhưng nó chỉ có thể đọc byte: từng byte riêng lẻ hoặc một vài byte sử dụng bộ đệm. Tùy chọn này không phù hợp với chúng tôi - chúng tôi muốn đọc các ký tự. Chức năng chúng ta cần đã được triển khai trong lớp trừu tượngReader . Điều này cũng có thể được nhìn thấy trong tài liệu. Паттерн проектирования «Адаптер» - 5Tuy nhiên, giao diện InputStream'a' và Reader'a' không tương thích! Như bạn có thể thấy, trong tất cả các triển khai phương thức, read()cả tham số được truyền và giá trị trả về đều khác nhau. Và đây là nơi chúng ta cần nó InputStreamReader! Anh ấy sẽ đóng vai trò là Người chuyển đổi giữa các lớp của chúng tôi. Như trong ví dụ về đầu đọc thẻ mà chúng ta đã xem xét ở trên, chúng ta chuyển đối tượng của lớp “đã thích ứng” “nội bộ”, tức là cho hàm tạo của lớp bộ điều hợp. Trong ví dụ trước, chúng ta đã truyền một đối tượng MemoryCardbên trong CardReader. Bây giờ chúng ta chuyển đối tượng InputStreamcho hàm tạo InputStreamReader! Về chất lượng, InputStreamchúng tôi sử dụng biến đã quen thuộc System.in:
public static void main(String[] args) {

   InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
Và thực sự: bằng cách xem tài liệu, InputStreamReaderchúng ta sẽ thấy rằng quá trình "chuyển thể" đã thành công :) Bây giờ chúng ta có các phương pháp tùy ý cho phép chúng ta đọc các ký tự. Паттерн проектирования «Адаптер» - 6Và mặc dù ban đầu đối tượng của chúng tôi System.in(một luồng liên kết với bàn phím) không cho phép điều này, nhưng bằng cách tạo mẫu Adaptor , những người tạo ra ngôn ngữ đã giải quyết được vấn đề này. Lớp trừu tượng Reader, giống như hầu hết các lớp I/O, có một người anh em song sinh - Writer. Nó có cùng một lợi thế lớn Reader- nó cung cấp một giao diện thuận tiện để làm việc với các ký hiệu. Với luồng đầu ra, vấn đề và giải pháp của nó trông giống như trong trường hợp luồng đầu vào. Có một lớp OutputStreamchỉ có thể ghi byte; Có một lớp trừu tượng Writercó thể hoạt động với các ký hiệu và có hai giao diện không tương thích. Vấn đề này một lần nữa được giải quyết thành công bằng mẫu Adaptor. Sử dụng một lớp, OutputStreamWriterchúng ta có thể dễ dàng “thích ứng” hai giao diện lớp Writervới OutputStreamnhau. Và, sau khi nhận được một luồng byte OutputStreamtrong hàm tạo, OutputStreamWritertuy nhiên, với sự trợ giúp, chúng ta có thể viết các ký tự chứ không phải 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();
   }
}
Chúng tôi đã viết một ký tự có mã 32144 - 綐 vào tệp của mình, do đó loại bỏ nhu cầu làm việc với byte :) Đó là tất cả cho ngày hôm nay, hẹn gặp lại các bạn trong các bài giảng tiếp theo! :)
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION