Header Ads

Seo Services

Hế lô anh em ✌️✌️✌️

Trong bài viết trước mình đã cùng anh em tìm hiểu về Factory Method - một trong những Creational Design Pattern đơn giản và được sử dụng khá nhiều.

Nếu anh em nào chưa đọc thì có thể tham khảo tại đây. Trong bài viết tiếp theo này mình sẽ cùng anh em tìm hiểu về một Creational Design Pattern nữa đó là Abstract Factory.

Vậy Abstract Factory được sinh ra để làm gì, có liên hệ gì với Factory Method không thì anh em cùng mình theo dõi đến cuối bài viết để hiểu rõ hơn nhé!

Okay, bắt đầu thôi nào ✈️✈️✈️

1. Đặt vấn đề

Trong bài viết về Factory Method mình đã lấy một ví dụ về việc Nạp, Rút tiền ở cây ATM. Và rõ ràng qua bài viết đó chúng ta rút ra được một điều đó là: 

"Bản chất của Method Factory để giải quyết vấn đề liên quan đến đối tượng (Object). Có nghĩa rằng nếu bài toán của chúng ta có nhiều đối tượng liên quan đến nhau nhưng lại thể hiện (triển khai) theo các cách khác nhau thì chúng ta nên nghĩ đến việc sử dụng Factory Method để chương trình dễ mở rộng cho các đối tượng khác sau này."

Câu hỏi là:"Okay, nhiều đối tượng thì dùng Method Factory vậy khi mỗi đối tượng lại có nhiều loại (Object Types) khác nhau thì sao?"

Câu hỏi này sẽ được trả lời bằng cách áp dụng Abstract Factory mà mình sắp giới thiệu với các bạn sau đây 😁. Okay văn vở nhiều rồi bây giờ đi vào ví dụ hôm nay nhé!

💡Mình muốn viết một chương trình quản lý cho các cửa hàng bán máy tính. Chúng ta biết rằng máy tính thì có rất nhiều loại nhưng để đơn giản mình giả sử chỉ có laptop và PC. Và tất nhiên laptop hay PC thì cũng được chia ra nhiều loại ví dụ như dòng Gaming, dòng văn phòng... Và cũng để đơn giản mình giả sử chỉ có hai dòng máy này thôi.

🙃Khách hàng đến mua hàng và họ không quan tâm đó là laptop hay PC mà chỉ quan tâm tới dòng máy Gaming hay văn phòng (thực tế chắc không vậy đâu nhưng mình lấy ví dụ như vậy 🤣)

🤔Vậy chúng ta giải quyết bài toán này như thế nào? Và nếu áp dụng Abstract Factory vào thì sẽ áp dụng như thế nào?

2. Phân tích

Hôm nay đặt vấn đề hơi dài xíu nhưng chắc qua đó anh em cũng phần nào hình dung được bài toán mà chúng ta sẽ sử dụng Abstract Factory rồi nhỉ! Bây giờ cùng mình phân tích nhé 👍

Chúng ta có hai đối tượng đó là Laptop và PC, với mỗi đối tượng này chúng ta lại có hai loại đó là dòng Gaming và dòng Office. Tức là thực tế chúng ta có 4 đối tượng Gaming Laptop, Office Laptop, Gaming PC và Office PC.

Vì vậy chúng ta sẽ định nghĩa hai interface, một là Laptop để hai đối tượng Gaming Laptop, Office Laptop sẽ triển khai interface này. Tiếp đó là interface PC để hai đối tượng Gaming PC, Office PC cũng sẽ lần lượt triển khai interface này.

Khách hàng (có thể coi là client) chỉ quan tâm đến dòng máy là Gaming hay Office nên chúng ta sẽ tạo ra các factory tương ứng cho hai dòng này.

Đến đây với tư tưởng cũ từ Factory Method chúng ta sẽ định nghĩa một Abstact Factory dùng để định nghĩa các factory cho hai dòng máy trên.

Anh em có thể xem diagram mình vẽ bên dưới để dễ hình dung hơn. 

Đơn giản anh em có thể hiểu Abstract Factory giống factory của các factory vậy. Bây giờ mình sẽ cùng anh em code theo diagram bên trên xem việc triển khai Abstract Factory sẽ như thế nào nhé ✈️.

3. Ví dụ

3.1 - Định nghĩa các interface

Chúng ta có hai interface đó là LaptopPC với các phương thức tương ứng:

public interface Laptop {
    Integer getPrice();
    Double getWeight();
    String getLabel();
}

public interface PC {
    Integer getPrice();
    Double getWeight();
}

3.2 - Định nghĩa các lớp (đối tượng) sẽ triển khai các interface

Với interface Laptop chúng ta có hai lớp sẽ triển khai interface này:

public class GamingLaptop implements Laptop {
    private static final GamingLaptop INSTANCE = new GamingLaptop();

    private GamingLaptop() {}

    public static GamingLaptop getInstance() {
        return INSTANCE;
    }

    @Override
    public Integer getPrice() {
        return 1200;
    }

    @Override
    public Double getWeight() {
        return 2.4;
    }

    @Override
    public String getLabel() {
        return "ASUS";
    }
}

public class OfficeLaptop implements Laptop {
    public static final OfficeLaptop INSTANCE = new OfficeLaptop();

    private OfficeLaptop() {}

    public static OfficeLaptop getInstance() {
        return INSTANCE;
    }

    @Override
    public Integer getPrice() {
        return 800;
    }

    @Override
    public Double getWeight() {
        return 1.2;
    }

    @Override
    public String getLabel() {
        return "DELL";
    }
}

Tương tự với interface PC cũng vậy:

public class GamingPC implements PC {
    private static final GamingPC INSTANCE = new GamingPC();

    private GamingPC() {}

    public static GamingPC getInstance() {
        return INSTANCE;
    }

    @Override
    public Integer getPrice() {
        return 2400;
    }

    @Override
    public Double getWeight() {
        return 10.5;
    }
}

public class OfficePC implements PC {
    public static final OfficePC INSTANCE = new OfficePC();

    private OfficePC() {}

    public static OfficePC getInstance() {
        return INSTANCE;
    }

    @Override
    public Integer getPrice() {
        return 1000;
    }

    @Override
    public Double getWeight() {
        return 8.5;
    }
}

Note: Ở những class này mình có sử dụng Singleton Pattern cho mục đích khởi tạo các object. Anh em nào chưa nắm được về pattern này thì cứ new cật lực cũng không sao nhé 😁 Mình sẽ cùng anh em tìm hiểu về pattern này trong các bài viết tiếp theo!

3.3 - Định nghĩa Abstract Factory

Đây sẽ là lớp mà client làm việc trực tiếp và nhiệm vụ của nó là tạo ra các factory tương ứng với request từ client. Mỗi factory này đóng vai trò như một Factory Method trong pattern cùng tên mà mình đã đề cập trong bài viết về Factory Method.

public abstract class ComputerAbstractFactory {
    abstract Laptop saleLaptop();
    abstract PC salePC();

    public static ComputerAbstractFactory defineFactory(String factoryType) {
        switch (factoryType) {
            case "GAMING":
                return new GamingFactory();
            case "OFFICE":
                return new OfficeFactory();
            default:
                throw new UnsupportedOperationException("Mặt hàng đã ngừng kinh doanh");
        }
    }
}

Note: Sở dĩ mình sử dụng abstract class thay vì một class bình thường thì mình cũng đã giải thích trong bài viết về Factory Method rồi. Nếu bạn nào chưa hiểu thì có thể xem lại nhé!

3.4 - Định nghĩa các factory tương ứng 

Chúng ta đã có một abstract factory và bây giờ chúng ta phải định nghĩa các factory tương ứng cho từng dòng máy. Và ở đây là hai factory GamingFactoryOfficeFactory như sau:

public class GamingFactory extends ComputerAbstractFactory {
    @Override
    public Laptop saleLaptop() {
        return GamingLaptop.getInstance();
    }

    @Override
    public PC salePC() {
        return GamingPC.getInstance();
    }
}

public class OfficeFactory extends ComputerAbstractFactory {
    @Override
    public Laptop saleLaptop() {
        return OfficeLaptop.getInstance();
    }

    @Override
    public PC salePC() {
        return OfficePC.getInstance();
    }
}

3.5 - Tương tác với client

Cuối cùng chúng ta xem sẽ tương tác với client như thế nào nhé?

public class App {

    public static class COMPUTER_TYPE {
        public static final String GAMING = "GAMING";
        public static final String OFFICE = "OFFICE";
    }

    private static ComputerAbstractFactory factory;

    public static void main(String[] args) {

        Scanner in = new Scanner(System.in);
        System.out.printf("%s%n%s%n", "1. DÒNG GAMING", "2. DÒNG OFFICE");

        int opt = in.nextInt();

        switch (opt) {
            case 1:
                factory = ComputerAbstractFactory.defineFactory(COMPUTER_TYPE.GAMING);
                break;
            case 2:
                factory = ComputerAbstractFactory.defineFactory(COMPUTER_TYPE.OFFICE);
                break;
        }

        Laptop laptop = factory.saleLaptop();
        System.out.printf("%s%n%s%n%s%n%s%n", "-> LAPTOP: ", "Khối lượng: " + laptop.getWeight(),  "Giá:" + laptop.getPrice(), "Thương hiệu: " + laptop.getLabel());

        PC pc = factory.salePC();
        System.out.printf("%s%n%s%n%s%n", "-> PC: ", "Khối lượng: " + pc.getWeight(), "Giá:" + pc.getPrice());
    }
}

Rõ ràng đúng như yêu cầu, client chỉ quan tâm hoặc là dòng Gaming hoặc là Office và nhiệm vụ của chúng ta là phải trả ra các sản phẩm tương ứng cho họ (ở đây có thể là laptop hoặc PC)

Chạy chương trình chúng ta sẽ có các kết quả như sau:

- Trường hợp mà client chọn dòng Gaming:

1. DÒNG GAMING
2. DÒNG OFFICE
1
-> LAPTOP: 
Khối lượng: 2.4
Giá:1200
Thương hiệu: ASUS
-> PC: 
Khối lượng: 10.5
Giá:2400

- Trường hợp mà client chọn dòng Office:

1. DÒNG GAMING
2. DÒNG OFFICE
2
-> LAPTOP: 
Khối lượng: 1.2
Giá:800
Thương hiệu: DELL
-> PC: 
Khối lượng: 8.5
Giá:1000

4. Kết luận

Áp dụng Abstract Factory giúp chúng ta giải quyết được vấn đề liên quan tới việc bài toán có nhiều đối tượng và các đối tượng này lại được chia thành nhiều loại khác nhau.

Trong thực tế có rất nhiều ví dụ mà chúng ta có thể gặp như ví dụ về các đồ nội thất trong nhà: Bàn, Ghế... cũng được chia thành nhiều mẫu như mẫu hiện đại, mẫu cổ điển...

Anh em có thể bắt gặp ví dụ này trong bài viết trên trang https://refactoring.guru/

Hoặc một ví dụ khác trong bài bài viết trước về Factory Method của mình liên quan đến việc việc Nạp, Rút tiền anh em có thể mở rộng trong trường hợp Nạp hay Rút tiền cũng có nhiều loại khác nhau (Nạp, Rút qua thẻ hoặc là tại quầy giao dịch...)

5. Tham khảo

https://refactoring.guru/design-patterns/abstract-factory

https://sourcemaking.com/design_patterns/abstract_factory 

Hẹn gặp lại anh em trong các bài viết tiếp theo nhé, byeee! 👋👋👋

Không có nhận xét nào:

Được tạo bởi Blogger.