Tập trung vào nội dung chính;
Theo góc nhìn của lập trình viên, mẫu nhà máy trừu tượng là một lớp trừu tượng quản lý các giao diện khác nhau, được gọi thông qua lớp trừu tượng này khi sử dụng!
Mẫu này có nhược điểm rõ ràng: khi cần thêm một chuỗi sản phẩm mới, phải sửa đổi khai báo lớp trừu tượng và thêm các phương thức triển khai, đồng thời mang theo nhược điểm của mẫu nhà máy. Trong thời đại hiện nay, khi số dòng code càng ít càng tốt, đa phần người lập trình sẽ thay thế bằng cấu trúc if...else. Vì những lý do trên, mẫu này ít được sử dụng trong thực tế!
Dưới đây là giải thích chi tiết từ nguồn khác giúp hiểu rõ hơn về khái niệm lớp trừu tượng!
Mẫu nhà máy trừu tượng
Mẫu nhà máy trừu tượng (Abstract Factory Pattern) là một siêu nhà máy tạo ra các nhà máy khác. Siêu nhà máy này còn được gọi là nhà máy của các nhà máy. Mẫu thiết kế này thuộc nhóm tạo mẫu, cung cấp cách tạo đối tượng tối ưu nhất.
Trong mẫu này, giao diện chịu trách nhiệm tạo ra một chuỗi đối tượng liên quan mà không cần chỉ định cụ thể lớp của chúng. Mỗi nhà máy sinh ra đều có thể tạo đối tượng theo kiểu nhà máy.
Giới thiệu
Mục đích: Cung cấp giao diện để tạo ra một chuỗi đối tượng liên quan hoặc phụ thuộc lẫn nhau mà không cần chỉ định cụ thể lớp của chúng.
Vấn đề chính: Giải quyết vấn đề lựa chọn giao diện phù hợp.
Khi nào sử dụng: Hệ thống có nhiều dòng sản phẩm khác nhau, nhưng chỉ sử dụng một dòng sản phẩm cụ thể.
Cách giải quyết: Định nghĩa nhiều sản phẩm trong cùng một dòng sản phẩm.
Mã nguồn quan trọng: Tích hợp nhiều sản phẩm cùng loại trong một nhà máy.
Ví dụ thực tế: Khi đi làm, để tham dự các buổi tiệc, bạn sẽ có ít nhất hai bộ trang phục. Ví dụ như trang phục công sở (một bộ sản phẩm cụ thể), trang phục thời trang (một bộ sản phẩm cụ thể). Đối với một gia đình, có thể có trang phục công sở cho nữ, công sở cho nam, thời trang cho nữ, thời trang cho nam, tất cả đều là các bộ sản phẩm cụ thể. Giả sử trong một tình huống (dù không tồn tại trong thực tế), tủ đồ (nhà máy cụ thể) trong nhà bạn chỉ chứa một loại trang phục (một bộ sản phẩm cụ thể), mỗi lần lấy trang phục sẽ lấy từ tủ đó. Áp dụng tư duy lập trình hướng đối tượng, tất cả các tủ đồ (nhà máy cụ thể) đều là một dạng của lớp tủ đồ (nhà máy trừu tượng), trong khi mỗi bộ trang phục cụ thể bao gồm áo khoác (một sản phẩm cụ thể), quần (một sản phẩm cụ thể), và các sản phẩm áo khoác cụ thể đều là áo khoác (sản phẩm trừu tượng), quần cụ thể đều là quần (một sản phẩm trừu tượng khác).
Ưu điểm: Khi nhiều đối tượng trong cùng một dòng sản phẩm được thiết kế để làm việc cùng nhau, mẫu này đảm bảo khách hàng luôn chỉ sử dụng các đối tượng thuộc cùng một dòng sản phẩm.
Nhược điểm: Khó mở rộng dòng sản phẩm, muốn thêm một sản phẩm mới của dòng, phải sửa đổi cả lớp Creator trừu tượng và lớp cụ thể.
Tình huống sử dụng: 1. Thay đổi giao diện QQ, thay đổi toàn bộ theo bộ. 2. Tạo ứng dụng cho nhiều hệ điều hành khác nhau.
Lưu ý: Dòng sản phẩm khó mở rộng, cấp độ sản phẩm dễ mở rộng.
Thực hiện
Chúng ta sẽ tạo giao diện Figure và Tone, cùng các lớp cụ thể triển khai giao diện này. Tiếp theo là tạo lớp trừu tượng AbstractFactory. Sau đó định nghĩa các lớp nhà máy FigureFactory và ToneFactory, đều kế thừa từ AbstractFactory. Sau đó tạo lớp tạo nhà máy FactoryProducer.
Lớp AbstractFactoryPatternDemo sẽ sử dụng FactoryProducer để lấy đối tượng AbstractFactory. Nó sẽ truyền thông tin hình dạng Figure (CIRCLE / RECTANGLE / SQUARE) cho AbstractFactory để lấy loại đối tượng cần thiết. Đồng thời truyền thông tin màu sắc Tone (RED / GREEN / BLUE) cho AbstractFactory để lấy loại đối tượng cần thiết.
//Bước 1
//Tạo giao diện cho hình dạng.
Figure.java
public interface Figure {
void render();
}
//Bước 2
//Tạo các lớp cụ thể triển khai giao diện.
//RectangleShape.java
public class RectangleShape implements Figure {
@Override
public void render() {
System.out.println("Inside RectangleShape::render() method.");
}
}
//SquareShape.java
public class SquareShape implements Figure {
@Override
public void render() {
System.out.println("Inside SquareShape::render() method.");
}
}
//CircleShape.java
public class CircleShape implements Figure {
@Override
public void render() {
System.out.println("Inside CircleShape::render() method.");
}
}
//Bước 3
//Tạo giao diện cho màu sắc.
//Tone.java
public interface Tone {
void applyColor();
}
//Bước 4
//Tạo các lớp cụ thể triển khai giao diện.
//RedTone.java
public class RedTone implements Tone {
@Override
public void applyColor() {
System.out.println("Inside RedTone::applyColor() method.");
}
}
//GreenTone.java
public class GreenTone implements Tone {
@Override
public void applyColor() {
System.out.println("Inside GreenTone::applyColor() method.");
}
}
//BlueTone.java
public class BlueTone implements Tone {
@Override
public void applyColor() {
System.out.println("Inside BlueTone::applyColor() method.");
}
}
//Bước 5
//Tạo lớp trừu tượng để lấy nhà máy cho đối tượng Figure và Tone.
//AbstractFactory.java
public abstract class AbstractFactory {
abstract Tone getTone(String tone);
abstract Figure getFigure(String figure) ;
}
//Bước 6
//Tạo các lớp nhà máy kế thừa AbstractFactory, tạo đối tượng cụ thể dựa trên thông tin được cung cấp.
//FigureFactory.java
public class FigureFactory extends AbstractFactory {
@Override
public Figure getFigure(String figureType){
if(figureType == null){
return null;
}
if(figureType.equalsIgnoreCase("CIRCLE")){
return new CircleShape();
} else if(figureType.equalsIgnoreCase("RECTANGLE")){
return new RectangleShape();
} else if(figureType.equalsIgnoreCase("SQUARE")){
return new SquareShape();
}
return null;
}
@Override
Tone getTone(String tone) {
return null;
}
}
//ToneFactory.java
public class ToneFactory extends AbstractFactory {
@Override
public Figure getFigure(String figureType){
return null;
}
@Override
Tone getTone(String tone) {
if(tone == null){
return null;
}
if(tone.equalsIgnoreCase("RED")){
return new RedTone();
} else if(tone.equalsIgnoreCase("GREEN")){
return new GreenTone();
} else if(tone.equalsIgnoreCase("BLUE")){
return new BlueTone();
}
return null;
}
}
//Bước 7
//Tạo lớp tạo nhà máy, lấy nhà máy dựa trên thông tin hình dạng hoặc màu sắc được truyền vào.
//FactoryProducer.java
public class FactoryProducer {
public static AbstractFactory getFactory(String choice){
if(choice.equalsIgnoreCase("FIGURE")){
return new FigureFactory();
} else if(choice.equalsIgnoreCase("TONE")){
return new ToneFactory();
}
return null;
}
}
//Bước 8
//Sử dụng FactoryProducer để lấy AbstractFactory, tạo đối tượng cụ thể dựa trên thông tin được truyền vào.
//AbstractFactoryPatternDemo.java
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
//Lấy nhà máy hình dạng
AbstractFactory figureFactory = FactoryProducer.getFactory("FIGURE");
//Lấy đối tượng hình dạng là Circle
Figure figure1 = figureFactory.getFigure("CIRCLE");
//Gọi phương thức render của Circle
figure1.render();
//Lấy đối tượng hình dạng là Rectangle
Figure figure2 = figureFactory.getFigure("RECTANGLE");
//Gọi phương thức render của Rectangle
figure2.render();
//Lấy đối tượng hình dạng là Square
Figure figure3 = figureFactory.getFigure("SQUARE");
//Gọi phương thức render của Square
figure3.render();
//Lấy nhà máy màu sắc
AbstractFactory toneFactory = FactoryProducer.getFactory("TONE");
//Lấy đối tượng màu sắc là Red
Tone tone1 = toneFactory.getTone("RED");
//Gọi phương thức applyColor của Red
tone1.applyColor();
//Lấy đối tượng màu sắc là Green
Tone tone2 = toneFactory.getTone("Green");
//Gọi phương thức applyColor của Green
tone2.applyColor();
//Lấy đối tượng màu sắc là Blue
Tone tone3 = toneFactory.getTone("BLUE");
//Gọi phương thức applyColor của Blue
tone3.applyColor();
}
}
//Bước 9
//Kiểm tra kết quả đầu ra.
Inside CircleShape::render() method.
Inside RectangleShape::render() method.
Inside SquareShape::render() method.
Inside RedTone::applyColor() method.
Inside GreenTone::applyColor() method.
Inside BlueTone::applyColor() method.