Trước tiên, chúng ta hãy cùng cafedev giới thiệu mọi thứ về Factory Method Design Pattern và phần code ví dụ chi tiết nhằm giúp ace dễ hiểu khi áp dụng trên các ngôn ngữ khác nhau. Ace có thể tham khảo thêm các bài khác tại series Design Pattern tại đây.

Factory Method Pattern là creational design pattern, tức là, liên quan đến việc tạo đối tượng. Trong Factory pattern, chúng ta tạo đối tượng mà không để lộ logic tạo cho khách và khách sử dụng cùng một giao diện chung để tạo loại đối tượng mới.

Ý tưởng là sử dụng một hàm thành viên tĩnh (phương thức nhà máy tĩnh) để tạo và trả về các thể hiện, ẩn chi tiết của các mô-đun lớp khỏi người dùng.

Factory pattern là một trong những nguyên tắc thiết kế cốt lõi để tạo một đối tượng, cho phép khách hàng tạo các đối tượng của thư viện (được giải thích bên dưới) theo cách mà nó không kết hợp chặt chẽ với hệ thống phân cấp lớp của thư viện.

Điều gì có nghĩa là khi chúng ta nói về thư viện và khách hàng?

Thư viện là thứ được cung cấp bởi một số bên thứ ba, nó hiển thị một số API công khai và khách hàng thực hiện các lệnh gọi đến các API công khai đó để hoàn thành nhiệm vụ của nó. Một ví dụ rất đơn giản có thể là các loại Chế độ xem khác nhau được cung cấp bởi Hệ điều hành Android.

Tại sao mô hình nhà máy?

Hãy để chúng tôi hiểu nó với một ví dụ:

// A design without factory pattern 
#include <iostream> 
using namespace std; 

// Library classes 
class Vehicle { 
public: 
	virtual void printVehicle() = 0; 
}; 
class TwoWheeler : public Vehicle { 
public: 
	void printVehicle() { 
		cout << "I am two wheeler" << endl; 
	} 
}; 
class FourWheeler : public Vehicle { 
	public: 
	void printVehicle() { 
		cout << "I am four wheeler" << endl; 
	} 
}; 

// Client (or user) class 
class Client { 
public: 
	Client(int type) { 

		// Client explicitly creates classes according to type 
		if (type == 1) 
			pVehicle = new TwoWheeler(); 
		else if (type == 2) 
			pVehicle = new FourWheeler(); 
		else
			pVehicle = NULL; 
	} 

	~Client() { 
		if (pVehicle) 
		{ 
			delete[] pVehicle; 
			pVehicle = NULL; 
		} 
	} 

	Vehicle* getVehicle() { 
		return pVehicle; 
	} 
private: 
	Vehicle *pVehicle; 
}; 

// Driver program 
int main() { 
	Client *pClient = new Client(1); 
	Vehicle * pVehicle = pClient->getVehicle(); 
	pVehicle->printVehicle(); 
	return 0; 
} 

Đầu ra:

I am two wheeler

Những vấn đề với thiết kế trên là gì?

Như bạn đã quan sát trong ví dụ trên, Client tạo các đối tượng của TwoWheeler hoặc FourWheeler dựa trên một số đầu vào trong quá trình xây dựng đối tượng của nó.

Giả sử, thư viện giới thiệu một loại ThreeWheeler mới để kết hợp cả xe ba bánh. Chuyện gì sẽ xảy ra? Máy khách sẽ kết thúc chuỗi một cái khác mới nếu trong bậc thang có điều kiện để tạo các đối tượng của ThreeWheeler. Đến lượt nó, Client sẽ cần được biên dịch lại. Vì vậy, mỗi khi một thay đổi mới được thực hiện ở phía thư viện, Khách hàng sẽ cần thực hiện một số thay đổi tương ứng ở phần cuối của nó và biên dịch lại mã. Nghe tệ thật? Đây là một thực hành thiết kế rất tệ.

Làm thế nào để tránh vấn đề?

Câu trả lời là, hãy tạo một phương thức static (hoặc factory). Hãy cho chúng tôi xem mã dưới đây.

// C++ program to demonstrate factory method design pattern 
#include <iostream> 
using namespace std; 

enum VehicleType { 
	VT_TwoWheeler, VT_ThreeWheeler, VT_FourWheeler 
}; 

// Library classes 
class Vehicle { 
public: 
	virtual void printVehicle() = 0; 
	static Vehicle* Create(VehicleType type); 
}; 
class TwoWheeler : public Vehicle { 
public: 
	void printVehicle() { 
		cout << "I am two wheeler" << endl; 
	} 
}; 
class ThreeWheeler : public Vehicle { 
public: 
	void printVehicle() { 
		cout << "I am three wheeler" << endl; 
	} 
}; 
class FourWheeler : public Vehicle { 
	public: 
	void printVehicle() { 
		cout << "I am four wheeler" << endl; 
	} 
}; 

// Factory method to create objects of different types. 
// Change is required only in this function to create a new object type 
Vehicle* Vehicle::Create(VehicleType type) { 
	if (type == VT_TwoWheeler) 
		return new TwoWheeler(); 
	else if (type == VT_ThreeWheeler) 
		return new ThreeWheeler(); 
	else if (type == VT_FourWheeler) 
		return new FourWheeler(); 
	else return NULL; 
} 

// Client class 
class Client { 
public: 

	// Client doesn't explicitly create objects 
	// but passes type to factory method "Create()" 
	Client() 
	{ 
		VehicleType type = VT_ThreeWheeler; 
		pVehicle = Vehicle::Create(type); 
	} 
	~Client() { 
		if (pVehicle) { 
			delete[] pVehicle; 
			pVehicle = NULL; 
		} 
	} 
	Vehicle* getVehicle() { 
		return pVehicle; 
	} 

private: 
	Vehicle *pVehicle; 
}; 

// Driver program 
int main() { 
	Client *pClient = new Client(); 
	Vehicle * pVehicle = pClient->getVehicle(); 
	pVehicle->printVehicle(); 
	return 0; 
} 

Đầu ra:

I am three wheeler

Trong ví dụ trên, chúng ta đã hoàn toàn tách riêng việc lựa chọn kiểu để tạo đối tượng từ Máy khách. Thư viện bây giờ chịu trách nhiệm quyết định loại đối tượng nào sẽ tạo dựa trên đầu vào. Khách hàng chỉ cần thực hiện cuộc gọi đến nhà máy của thư viện Phương thức tạo và chuyển kiểu mà nó muốn mà không cần lo lắng về việc thực hiện thực tế việc tạo đối tượng.

Cảm ơn Rumplestiltskin đã cung cấp lời giải thích ở trên.

Các ví dụ khác về Factory Method:

  1. Giả sử, trong hệ thống ‘Vẽ’, tùy thuộc vào đầu vào của người dùng, có thể vẽ các hình khác nhau như hình vuông, hình chữ nhật, hình tròn. Ở đây chúng ta có thể sử dụng phương thức factory để tạo các phiên bản tùy thuộc vào đầu vào của người dùng. Để thêm kiểu hình dạng mới, không cần thay đổi mã của khách hàng.
  2. Một ví dụ khác: Trong trang web du lịch, chúng ta có thể đặt vé tàu cũng như vé xe buýt và vé máy bay. Trong trường hợp này, người dùng có thể đặt loại hành trình của mình là ‘xe buýt’, ‘xe lửa’ hoặc ‘chuyến bay’.
    Ở đây chúng ta có một lớp trừu tượng ‘AnyTravel’ với một hàm thành viên tĩnh ‘GetObject’, tùy thuộc vào kiểu du lịch của người dùng, sẽ tạo và trả về đối tượng ‘BusTravel’ hoặc ‘TrainTravel’. ‘BusTravel’ hoặc ‘TrainTravel’ có các chức năng phổ biến như tên hành khách, điểm xuất phát, tham số điểm đến.

Cài ứng dụng cafedev để dễ dàng cập nhật tin và học lập trình mọi lúc mọi nơi tại đây.

Tài liệu từ cafedev:

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!