NỘI DUNG BÀI VIẾT
Định nghĩa
- Định nghĩa giao diện cho việc khởi tạo một đối tượng, nhưng để lớp con quyết định lớp nào sẽ được khởi tạo. Factory Method Factory Method giao việc khởi tạo một đối tượng cụ thể cho lớp con.
- Định nghĩa một hàm tạo ảo.
- Toán tử new không dùng trong trường hợp này.
Vấn đề
Một bộ khung (framework) cần làm chuẩn hóa mô hình kiến trúc cho nhiều ứng dụng, nhưng cho phép các ứng dụng riêng biệt định nghĩa các đối tượng của nó và cung cấp khởi tạo cho chúng.
Thảo luận
Factory Method tạo ra các đối tượng như là Template Method để cài đặt các thuật toán theo ý muốn. Class cha sẽ chỉ rõ tất cả các chuẩn và cách xử lý chung (sử dụng các hàm ảo là “placeholders” – phần giữ chỗ cho các bước khởi tạo), sau đó ủy nhiệm, giao phú việc tạo chi tiết cho các lớp con được cung cấp bởi client.
Factory Method tạo các thiết kế có thể dễ dàng chỉnh sửa và có ít độ phức tạp. Các design pattern khác yêu cầu các class mới, riêng Factory Method chỉ yêu cầu một operation(thao tác, hoạt động) mới.
Mọi người thường sử dung Factory Method như một chuẩn để tạo ra các đối tượng; thức sự nó không cần thiết nếu: Việc khởi tạo của class đó không thay đổi, hoặc việc khởi tạo đặt ở trong một operation mà class con có thể dễ dàng ghi đè lên(như khởi tạo một operation).
Factory Method tương tự như Abstract Factory nhưng không nhấn mạnh việc tạo các đối tượng cùng một họ.
Các Factory Mothod thường được xác định bởi một khung kiến trúc, sau đó được thực thi bởi người dùng của khung đó.
Cấu trúc
Việc áp dụng cài đặt Factory Method được thảo luận trong Gang of Four (bên dưới) phần lớn giống với Abstract Factory. Vì thế, các trình bày trong bài này này chủ yếu tập trung vào phương pháp tiếp cận đã trở nên phổ biến từ trước.
Một định nghĩa ngày càng phổ biến của Factory Method là: một phương thức static của class trả về một đối tượng của kiểu class. Không giống như constructor(hàm tạo), đối tượng thực sự trả về có thể là một thể hiện của lớp con. Cũng không giống như constructor, một đối tượng tồn tại có thể sử dụng lại, thay vì phải khởi tạo một đối tượng mới. Và cũng không giống như constructor, Factory Method có thể có nhiều tên, mô tả khác nhau (ví dụ: Color.make_RGB_color(float red, float green, float blue) và Color.make_HSB_color(float hue, float saturation, float brightness)
Client hoàn toàn tách rời từ các cài đặt chi tiết của lớp thừa kế. Đa hình việc khởi tạo là có thể.
Ví dụ
Factory Method định nghĩa một giao diện cho việc khởi tạo đối tượng, nhưng để lớp con quyết định lớp nào được khởi tạo. Máy khuôn ép nhựa mô tả pattern này. Các nhà sản xuất đồ chơi bằng nhựa xử lý bột nhựa và đưa chúng vào khuôn có hình dạng mong muốn. Lớp của đồ chơi (ô tô,… ) được xác định bởi khuôn đó.
Kiểm tra
- Nếu bạn có một hệ thống phân cấp thừa kế trong các đa hình, xem xét thêm khả năng tạo đa hình bằng cách định nghĩa một static factory me thod trong lớp cơ sở.
- Thiết các đối số cho factory method. Những phẩm chất hoặc đặc tính nào cần và đủ để xác định chính xác lớp thừa kế.
- Xem xét việc thiết kế một “object pool” nội bộ để cho phép các đối tượng được tái sử dụng thay vì tạo lại từ đầu
- Xem xét việc đặt các hàm tạo là private hoặc protected.
Kinh nghiệm
- Các lớp Abstract Factory có thể cài đặt với Factory Methods, nhưng cũng có thể cài đặt với Prototype.
- Các Factory Method thường được gọi bên trong các Template Method
- Factory Method: tạo thông qua thừa kế. Prototype: tạo thông qua ủy quyền.
- Thông thường, việc thiết kế bắt đầu với Factory Method (ít phức tạp, nhiều tùy biến, cài thiện, các lớp con nhiều) và tiến hóa thành Abstract Factory, Prototype, hoặc Builder (tính mềm dẻo, sự phức tạp hơn) khi các lập trình viên nhận ra rằng tính mềm dẻo là cần thiết.
- Prototype không yêu cầu lớp con, nhưng nó yêu cầu một toán tử khởi tạo. Factory Method yêu cầu lớp con, nhưng không yêu cầu khởi tạo.
- Ưu điểm của Factory Method là có thể trả về cùng một đối tượng nhiều lần, hoặc có thể trả về một lớp con hơn là một đối tượng có kiểu chính xác.
- Chủ trương của Factory Method thì đây là vấn đề chính của ngôn ngữ thiết kế(khuyết điểm) rằng tất cả các hàm tạo phải để là private hoặc protected. Đó là công việc không ai khác là lớp sản xuất tạo ra một đối tượng mới hoặc tái sử dụng lại đối tượng cũ.
- Toán tử new thì thực sự gây nguy hại. Có sự khác nhau giữa yêu cầu một đối tượng hoặc khởi tạo nó. Toán tử new luôn luôn tạo đối tượng và không đóng gói đối tượng khởi tạo. Factory Method đảm bảo việc thực hiện đóng gói đó, và cho phép một đối tượng được yêu cầu mà không cần có sự liên kết với hành động được tạo ra.
Ví dụ cài đặt thực hiện Factory Design Pattern trong Java
Trong lập trình class-based (dựa trên lớp), factory method là mẫu khởi tạo sử dụng factoy method để giải quyết vấn đề tạo ra các đối tượng mà không cần phải xác định chính xác lớp của đối tượng được tạo ra. Điều này được thực hiện bằng việc tạo ra các đối tượng thông qua việc gọi một phương thức factory method—hoặc được xác định trong một giao diện(interface) và thực hiện bởi các lớp con, hoặc thực hiện trong lớp cơ sở và được ghi đè bởi lớp dẫn xuất—thay vì bằng cách gọi một hàm tạo(constructor).
interface ImageReader { DecodedImage getDecodeImage(); }
class DecodedImage { private String image; public DecodedImage(String image) { this.image = image; } @Override public String toString() { return image + ": is decoded"; } }
class GifReader implements ImageReader { private DecodedImage decodedImage; public GifReader(String image) { this.decodedImage = new DecodedImage(image); } @Override public DecodedImage getDecodeImage() { return decodedImage; } }
class JpegReader implements ImageReader { private DecodedImage decodedImage; public JpegReader(String image) { decodedImage = new DecodedImage(image); } @Override public DecodedImage getDecodeImage() { return decodedImage; } }
public class FactoryMethodDemo { public static void main(String[] args) { DecodedImage decodedImage; ImageReader reader = null; String image = args[0]; String format = image.substring(image.indexOf('.') + 1, (image.length())); if (format.equals("gif")) { reader = new GifReader(image); } if (format.equals("jpeg")) { reader = new JpegReader(image); } assert reader != null; decodedImage = reader.getDecodeImage(); System.out.println(decodedImage); } }