Giới thiệu về mẫu Factory tĩnh
Mẫu thiết kế Factory tĩnh (còn gọi là Simple Factory Pattern) giúp giải quyết vấn đề khi thiết kế phần mềm cần mở rộng. Thay vì để client trực tiếp khởi tạo các đối tượng, chúng ta sử dụng một factory class để封装 quá trình khởi tạo. Điều này giúp code dễ bảo trì và mở rộng hơn.
Bài viết này sẽ hướng dẫn bạn cách triển khai mẫu Factory tĩnh thông qua ví dụ một ứng dụng máy tính đơn giản.
Lớp trừu tượng phép toán
/**
* Lớp trừu tượng đại diện cho các phép toán
*/
public abstract class TinhToan {
/**
* Số thứ nhất
*/
private double soA;
/**
* Số thứ hai
*/
private double soB;
/**
* Phương thức trừu tượng để tính kết quả
*
* @return kết quả phép toán
*/
public abstract double tinhKetQua();
/**
* Lấy giá trị số thứ nhất
*
* @return soA
*/
public double getSoA() {
return this.soA;
}
/**
* Thiết lập số thứ nhất
*
* @param soA số thứ nhất
*/
public void setSoA(double soA) {
this.soA = soA;
}
/**
* Lấy giá trị số thứ hai
*
* @return soB
*/
public double getSoB() {
return this.soB;
}
/**
* Thiết lập số thứ hai
*
* @param soB số thứ hai
*/
public void setSoB(double soB) {
this.soB = soB;
}
@Override
public String toString() {
return "TinhToan{" +
"soA=" + soA +
", soB=" + soB +
'}';
}
}
Các lớp triển khai phép toán
/**
* Lớp thực hiện phép cộng
*/
public class PhepCong extends TinhToan {
@Override
public double tinhKetQua() {
return super.getSoA() + super.getSoB();
}
}
/**
* Lớp thực hiện phép trừ
*/
public class PhepTru extends TinhToan {
@Override
public double tinhKetQua() {
return super.getSoA() - super.getSoB();
}
}
/**
* Lớp thực hiện phép nhân
*/
public class PhepNhan extends TinhToan {
@Override
public double tinhKetQua() {
return super.getSoA() * super.getSoB();
}
}
/**
* Lớp thực hiện phép chia
*/
public class PhepChia extends TinhToan {
@Override
public double tinhKetQua() {
return super.getSoA() / super.getSoB();
}
}
Lớp Factory tĩnh
/**
* Factory tĩnh để tạo các đối tượng phép toán
*/
public class TinhToanFactory {
/**
* Tạo đối tượng phép toán dựa trên toán tử
* @param toanTu toán tử (+ - * /)
* @return đối tượng phép toán tương ứng
*/
public static TinhToan taoPhepToan(String toanTu)
{
TinhToan tinhToan = null;
switch (toanTu)
{
case "+":
tinhToan = new PhepCong();
break;
case "-":
tinhToan = new PhepTru();
break;
case "*":
tinhToan = new PhepNhan();
break;
case "/":
tinhToan = new PhepChia();
break;
}
return tinhToan;
}
}
Mã nguồn phía Client
/**
* Ví dụ minh họa mẫu Factory tĩnh
*/
public class ViDuFactoryTinh {
public static void main(String[] args) {
TinhToan tinhToan = null;
double ketQua = 0;
// Phép cộng
tinhToan = TinhToanFactory.taoPhepToan("+");
tinhToan.setSoA(1.1);
tinhToan.setSoB(2.2);
ketQua = tinhToan.tinhKetQua();
System.out.println("Ket qua phep cong: " + ketQua);
// Phép trừ
tinhToan = TinhToanFactory.taoPhepToan("-");
tinhToan.setSoA(3.3);
tinhToan.setSoB(2.2);
ketQua = tinhToan.tinhKetQua();
System.out.println("Ket qua phep tru: " + ketQua);
// Phép nhân
tinhToan = TinhToanFactory.taoPhepToan("*");
tinhToan.setSoA(3.3);
tinhToan.setSoB(2.2);
ketQua = tinhToan.tinhKetQua();
System.out.println("Ket qua phep nhan: " + ketQua);
// Phép chia
tinhToan = TinhToanFactory.taoPhepToan("/");
tinhToan.setSoA(3.3);
tinhToan.setSoB(2.2);
ketQua = tinhToan.tinhKetQua();
System.out.println("Ket qua phep chia: " + ketQua);
}
}
Kết quả thực thi
Ket qua phep cong: 3.3000000000000003
Ket qua phep tru: 1.0999999999999996
Ket qua phep nhan: 7.26
Ket qua phep chia: 1.4999999999999998
Phân tích ưu nhược điểm
Ưu điểm:
- Giảm thiểu sự phụ thuộc giữa client và các lớp cụ thể
- Tập trung logic khởi tạo đối tượng tại một nơi duy nhất
- Dễ dàng thay đổi và mở rộng
Nhược điểm:
- Khi cần thêm phép toán mới, cần sửa đổi lớp Factory (vi phạm nguyên tắc Open-Close)
- Sử dụng switch/case có thể trở nên cồng kềnh khi số lượng phép toán tăng
Để khắc phục nhược điểm này, trong các bài tiếp theo chúng ta sẽ tìm hiểu về mẫu Factory Method và Abstract Factory.