CHÀO! Hôm nay chúng ta sẽ tiếp tục nghiên cứu về các mẫu thiết kế và chúng ta sẽ thảo luận về mẫu nhà máy trừu tượng . Mẫu thiết kế: Nhà máy trừu tượng - 1Đây là những gì chúng ta sẽ đề cập trong bài học:
  • Chúng ta sẽ thảo luận về một nhà máy trừu tượng là gì và mô hình này giải quyết vấn đề gì
  • Chúng tôi sẽ tạo bộ khung của ứng dụng đa nền tảng để đặt cà phê thông qua giao diện người dùng
  • Chúng ta sẽ nghiên cứu các hướng dẫn về cách sử dụng mẫu này, bao gồm cả việc xem sơ đồ và mã
  • Và như một phần thưởng, bài học này bao gồm một quả trứng Phục sinh ẩn sẽ giúp bạn học cách sử dụng Java để xác định tên của hệ điều hành và tùy thuộc vào kết quả, để thực hiện hành động này hay hành động khác.
Để hiểu đầy đủ về mẫu này, bạn cần phải thông thạo các chủ đề sau:
  • kế thừa trong Java
  • các lớp và phương thức trừu tượng trong Java

Nhà máy trừu tượng giải quyết những vấn đề gì?

Một nhà máy trừu tượng, giống như tất cả các mẫu nhà máy, giúp chúng tôi đảm bảo rằng các đối tượng mới được tạo chính xác. Chúng tôi sử dụng nó để quản lý việc "sản xuất" nhiều họ đối tượng được kết nối với nhau. Các họ khác nhau của các đối tượng được kết nối với nhau... Điều đó có nghĩa là gì? Đừng lo lắng: trong thực tế, mọi thứ đơn giản hơn bạn tưởng. Để bắt đầu, một họ các đối tượng được kết nối với nhau có thể là gì? Giả sử chúng ta đang phát triển một chiến lược quân sự liên quan đến một số loại đơn vị:
  • bộ binh
  • kỵ sĩ
  • cung thủ
Các loại đơn vị này được kết nối với nhau, bởi vì chúng phục vụ trong cùng một đội quân. Chúng ta có thể nói rằng các danh mục được liệt kê ở trên là một họ các đối tượng được kết nối với nhau. Chúng tôi hiểu điều này. Nhưng mẫu nhà máy trừu tượng được sử dụng để sắp xếp việc tạo ra nhiều họ đối tượng được kết nối với nhau. Không có gì phức tạp ở đây cả. Hãy tiếp tục với ví dụ về chiến lược quân sự. Nói chung, các đơn vị quân đội thuộc về một số bên tham chiến khác nhau. Tùy thuộc vào phe của họ, các đơn vị quân đội có thể khác nhau đáng kể về ngoại hình. Bộ binh, kỵ binh và cung thủ của quân đội La Mã không giống với bộ binh, kỵ binh và cung thủ của người Viking. Trong chiến lược quân sự, những người lính của các quân đội khác nhau là những gia đình khác nhau của các đối tượng được kết nối với nhau. Sẽ thật buồn cười nếu một lập trình viên' Sai lầm của ông đã khiến một người lính mặc quân phục Pháp thời Napoléon, súng hỏa mai sẵn sàng, được tìm thấy đang đi giữa hàng ngũ bộ binh La Mã. Mẫu thiết kế nhà máy trừu tượng là cần thiết chính xác để giải quyết vấn đề này. Không, không phải vấn đề bối rối có thể đến từ du hành thời gian, mà là vấn đề tạo ra nhiều nhóm đối tượng liên kết với nhau. Một nhà máy trừu tượng cung cấp một giao diện để tạo ra tất cả các sản phẩm có sẵn (một họ đối tượng). Một nhà máy trừu tượng thường có nhiều triển khai. Mỗi người trong số họ chịu trách nhiệm tạo ra các sản phẩm của một trong các gia đình. Chiến lược quân sự của chúng tôi sẽ bao gồm một nhà máy trừu tượng tạo ra những người lính bộ binh, cung thủ và kỵ binh trừu tượng, cũng như việc triển khai nhà máy này. Ví dụ, một nhà máy tạo ra lính lê dương La Mã và một nhà máy tạo ra lính Carthage. Trừu tượng hóa là nguyên tắc hướng dẫn quan trọng nhất của mẫu này. Khách hàng của nhà máy chỉ làm việc với nhà máy và các sản phẩm của nhà máy thông qua các giao diện trừu tượng. Do đó, bạn không cần phải suy nghĩ về những người lính hiện đang được tạo ra. Thay vào đó, bạn chuyển trách nhiệm này cho một số triển khai cụ thể của nhà máy trừu tượng.

Hãy tiếp tục tự động hóa quán cà phê của chúng tôi

Trong buổi học cuối cùng, chúng tôi đã nghiên cứu mẫu phương pháp xuất xưởng. Chúng tôi đã sử dụng nó để mở rộng kinh doanh cà phê và mở một số địa điểm mới. Hôm nay chúng ta sẽ tiếp tục hiện đại hóa công việc kinh doanh của mình. Sử dụng mẫu nhà máy trừu tượng, chúng tôi sẽ đặt nền tảng cho một ứng dụng máy tính để bàn mới để đặt cà phê trực tuyến. Khi viết một ứng dụng dành cho máy tính để bàn, chúng ta nên luôn nghĩ đến việc hỗ trợ đa nền tảng. Ứng dụng của chúng tôi phải hoạt động trên cả macOS và Windows (spoiler: Hỗ trợ cho Linux vẫn còn để bạn thực hiện như bài tập về nhà). Ứng dụng của chúng ta sẽ như thế nào? Khá đơn giản: nó sẽ là một biểu mẫu bao gồm trường văn bản, trường lựa chọn và nút. Nếu bạn có kinh nghiệm sử dụng các hệ điều hành khác nhau, chắc chắn bạn đã nhận thấy rằng các nút trên Windows được hiển thị khác với trên máy Mac. Như mọi thứ khác... Chà, hãy bắt đầu.
  • nút
  • Trường văn bản
  • trường lựa chọn
Tuyên bố miễn trừ trách nhiệm: Trong mỗi giao diện, chúng tôi có thể xác định các phương thức như onClick, onValueChangedhoặc onInputChanged. Nói cách khác, chúng ta có thể xác định các phương thức cho phép chúng ta xử lý các sự kiện khác nhau (nhấn nút, nhập văn bản, chọn giá trị trong hộp chọn). Tất cả những điều này được cố ý bỏ qua ở đây để không làm quá tải ví dụ và để làm cho nó rõ ràng hơn khi chúng ta nghiên cứu mô hình nhà máy. Hãy xác định các giao diện trừu tượng cho các sản phẩm của chúng tôi:

public interface Button {}
public interface Select {}
public interface TextField {}
Đối với mỗi hệ điều hành, chúng ta phải tạo các thành phần giao diện theo phong cách của hệ điều hành đó. Chúng tôi sẽ viết mã cho Windows và MacOS. Hãy tạo triển khai cho Windows:

public class WindowsButton implements Button {
}

public class WindowsSelect implements Select {
}

public class WindowsTextField implements TextField {
}
Bây giờ chúng tôi làm tương tự cho MacOS:

public class MacButton implements Button {
}

public class MacSelect implements Select {
}

public class MacTextField implements TextField {
}
Xuất sắc. Bây giờ chúng ta có thể tiếp tục với nhà máy trừu tượng của mình, nơi sẽ tạo ra tất cả các loại sản phẩm trừu tượng có sẵn:

public interface GUIFactory {

    Button createButton();
    TextField createTextField();
    Select createSelect();

}
Tuyệt vời. Như bạn có thể thấy, chúng tôi chưa làm bất cứ điều gì phức tạp. Mọi thứ sau đó cũng đơn giản. Bằng cách tương tự với các sản phẩm, chúng tôi tạo ra các triển khai nhà máy khác nhau cho mỗi HĐH. Hãy bắt đầu với Windows:

public class WindowsGUIFactory implements GUIFactory {
    public WindowsGUIFactory() {
        System.out.println("Creating GUIFactory for Windows OS");
    }

    public Button createButton() {
        System.out.println("Creating Button for Windows OS");
        return new WindowsButton();
    }

    public TextField createTextField() {
        System.out.println("Creating TextField for Windows OS");
        return new WindowsTextField();
    }

    public Select createSelect() {
        System.out.println("Creating Select for Windows OS");
        return new WindowsSelect();
    }
}
Chúng tôi đã thêm một số đầu ra giao diện điều khiển bên trong các phương thức và hàm tạo để minh họa rõ hơn những gì đang xảy ra. Bây giờ cho macOS:

public class MacGUIFactory implements GUIFactory {
    public MacGUIFactory() {
        System.out.println("Creating GUIFactory for macOS");
    }

    @Override
    public Button createButton() {
        System.out.println("Creating Button for macOS");
        return new MacButton();
    }

    @Override
    public TextField createTextField() {
        System.out.println("Creating TextField for macOS");
        return new MacTextField();
    }

    @Override
    public Select createSelect() {
        System.out.println("Creating Select for macOS");
        return new MacSelect();
    }
}
Lưu ý rằng mỗi chữ ký phương thức chỉ ra rằng phương thức trả về một kiểu trừu tượng. Nhưng bên trong các phương pháp, chúng tôi đang tạo ra các triển khai cụ thể của sản phẩm. Đây là nơi duy nhất chúng tôi kiểm soát việc tạo các phiên bản cụ thể. Bây giờ là lúc để viết một lớp cho biểu mẫu. Đây là một lớp Java có các trường là phần tử giao diện:

public class CoffeeOrderForm {
    private final TextField customerNameTextField;
    private final Select coffeeTypeSelect;
    private final Button orderButton;

    public CoffeeOrderForm(GUIFactory factory) {
        System.out.println("Creating coffee order form");
        customerNameTextField = factory.createTextField();
        coffeeTypeSelect = factory.createSelect();
        orderButton = factory.createButton();
    }
}
Một nhà máy trừu tượng tạo các phần tử giao diện được chuyển đến hàm tạo của biểu mẫu. Chúng tôi sẽ chuyển cài đặt gốc cần thiết cho hàm tạo để tạo các thành phần giao diện cho một hệ điều hành cụ thể.

public class Application {
    private CoffeeOrderForm coffeeOrderForm;

    public void drawCoffeeOrderForm() {
        // Determine the name of the operating system through System.getProperty()
        String osName = System.getProperty("os.name").toLowerCase();
        GUIFactory guiFactory;

        if (osName.startsWith("win")) { // For Windows
            guiFactory = new WindowsGUIFactory();
        } else if (osName.startsWith("mac")) { // For Mac
            guiFactory = new MacGUIFactory();
        } else {
            System.out.println("Unknown OS. Unable to draw form :(");
            return;
        }
        coffeeOrderForm = new CoffeeOrderForm(guiFactory);
    }

    public static void main(String[] args) {
        Application application = new Application();
        application.drawCoffeeOrderForm();
    }
}
Nếu chúng tôi chạy ứng dụng trên Windows, chúng tôi sẽ nhận được đầu ra sau:

Creating GUIFactory for Windows OS
Creating coffee order form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
Trên máy Mac, đầu ra sẽ như sau:

Creating GUIFactory for macOS
Creating coffee order form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
Trên Linux:

Unknown OS. Unable to draw form :( 
Và bây giờ chúng tôi tóm tắt. Chúng tôi đã viết khung của một ứng dụng dựa trên GUI trong đó các thành phần giao diện được tạo riêng cho HĐH có liên quan. Chúng tôi sẽ lặp lại chính xác những gì chúng tôi đã tạo:
  • Dòng sản phẩm bao gồm trường nhập, trường lựa chọn và nút.
  • Các triển khai khác nhau của dòng sản phẩm dành cho Windows và macOS.
  • Một nhà máy trừu tượng xác định giao diện để tạo sản phẩm của chúng tôi.
  • Hai triển khai của nhà máy của chúng tôi, mỗi triển khai chịu trách nhiệm tạo ra một dòng sản phẩm cụ thể.
  • Một biểu mẫu (một lớp Java) có các trường là các phần tử giao diện trừu tượng được khởi tạo với các giá trị cần thiết trong hàm tạo bằng cách sử dụng một nhà máy trừu tượng.
  • Lớp ứng dụng Bên trong lớp này, chúng ta tạo một biểu mẫu, chuyển cài đặt xuất xưởng mong muốn cho hàm tạo của nó.
Kết quả cuối cùng là chúng tôi đã triển khai mẫu nhà máy trừu tượng.

Nhà máy trừu tượng: cách sử dụng

Một nhà máy trừu tượng là một mẫu thiết kế để quản lý việc tạo ra các dòng sản phẩm khác nhau mà không bị ràng buộc với các lớp sản phẩm cụ thể. Khi sử dụng mẫu này, bạn phải:
  1. Định nghĩa các dòng sản phẩm. Giả sử chúng ta có hai trong số chúng:
    • SpecificProductA1,SpecificProductB1
    • SpecificProductA2,SpecificProductB2
  2. Đối với mỗi sản phẩm trong họ, hãy xác định một lớp trừu tượng (giao diện). Trong trường hợp của chúng tôi, chúng tôi có:
    • ProductA
    • ProductB
  3. Trong mỗi dòng sản phẩm, mỗi sản phẩm phải triển khai giao diện được xác định trong bước 2.
  4. Tạo một nhà máy trừu tượng, với các phương thức tạo từng sản phẩm được xác định trong bước 2. Trong trường hợp của chúng tôi, các phương thức này sẽ là:
    • ProductA createProductA();
    • ProductB createProductB();
  5. Tạo các triển khai nhà máy trừu tượng để mỗi triển khai kiểm soát việc tạo ra các sản phẩm của một họ duy nhất. Để làm điều này, bên trong mỗi lần triển khai của nhà máy trừu tượng, bạn cần triển khai tất cả các phương thức sáng tạo để chúng tạo và trả về các triển khai sản phẩm cụ thể.
Sơ đồ UML sau đây minh họa các hướng dẫn được nêu ở trên: Mẫu thiết kế: Nhà máy trừu tượng - 3Bây giờ chúng ta sẽ viết mã theo các hướng dẫn sau:

    // Define common product interfaces
    public interface ProductA {}
    public interface ProductB {}

    // Create various implementations (families) of our products
    public class SpecificProductA1 implements ProductA {}
    public class SpecificProductB1 implements ProductB {}

    public class SpecificProductA2 implements ProductA {}
    public class SpecificProductB2 implements ProductB {}

    // Create an abstract factory
    public interface AbstractFactory {
        ProductA createProductA();
        ProductB createProductB();
    }

    // Implement the abstract factory in order to create products in family 1
    public class SpecificFactory1 implements AbstractFactory {

        @Override
        public ProductA createProductA() {
            return new SpecificProductA1();
        }

        @Override
        public ProductB createProductB() {
            return new SpecificProductB1();
        }
    }

    // Implement the abstract factory in order to create products in family 2
    public class SpecificFactory2 implements AbstractFactory {

        @Override
        public ProductA createProductA() {
            return new SpecificProductA2();
        }

        @Override
        public ProductB createProductB() {
            return new SpecificProductB2();
        }
    }

Bài tập về nhà

Để củng cố tài liệu, bạn có thể làm 2 việc:
  1. Tinh chỉnh ứng dụng đặt cà phê để nó cũng hoạt động trên Linux.
  2. Tạo nhà máy trừu tượng của riêng bạn để sản xuất các đơn vị tham gia vào bất kỳ chiến lược quân sự nào. Đây có thể là một chiến lược quân sự lịch sử liên quan đến quân đội thực, hoặc một chiến lược giả tưởng với Orc, gnomes và yêu tinh. Điều quan trọng là chọn một cái gì đó mà bạn quan tâm. Hãy sáng tạo, in thông báo trên bảng điều khiển và tận hưởng việc tìm hiểu về các mẫu!