Nội dung bài viết
Lê Minh Trung Được phát triển dựa trên kiến thức đa hình trong phương pháp lập trình hướng đối tượng. Abstract Factory là một trong những Design Pattern thông dụng, làm các đoạn mã trở nên mềm giẻo và dễ bảo trì hơn.

Giới thiệu

Nếu bạn từng nghe về Design Pattern, bạn có bao giờ thắc mắc chúng là cái gì? Và tại sao chúng lại được nhắc nhiều đến vậy? Đừng lo lắng, tôi hi vọng rằng bài viết này sẽ giúp bạn giải quyết những thắc mắc ban đầu của mình. Với những ai đã biết về Design Pattern, có thể dùng bài viết này để tham khảo và trau dồi kiến thức.

Tiền đề bài viết

Tôi có được may mắn tiếp cận vấn đề này vào hai năm trước, trong lúc nghe một ai đó nói về tác dụng của nó, sự tò mò trong tôi thôi thúc mình tìm hiểu vấn đề. Và hôm nay, khi đã đi được một quãng đường, tôi quyết định chia sẻ lại kiến thức này với các bạn, cũng là cách mà tôi lưu lại những kiến thức của mình.

Đối tượng hướng đến

Đối tượng hướng đến là đọc giả có kiến thức cơ bản về một ngôn ngữ lập trình nhất định và đã có kiến thức về phương pháp lập trình hướng đối tượng. Trong bài viết này, tôi sử dụng ví dụ với ngôn ngữ lập trình C++, các ngôn ngữ khác như Java, C#, Ruby… đều có nguyên lý thực hiện tương tự.

Quan điểm cá nhân về khái niệm Design Pattern

Design Pattern là một khái niệm để nói về các phương pháp giải quyết vấn đề trong lập trình, được các lập trình viên ghi lại, hoặc truyền đạt lại dưới một hình thức nào đó, với mục đích giải quyết nhanh gọn, và lưu trữ cho thế hệ về sau có thể xử lý nhanh vấn đề. Trải qua thời gian, các Pattern ngày càng phong phú về thể loại cũng như hình thức tiếp cận để giải quyết vấn đề.

Chúng ta sẽ tiếp cận Design Pattern đầu tiên có tên là Abstract Factory. Lý do tôi chọn Design Pattern này làm bài viết đầu tiên bởi sự đơn giản, và dễ nắm bắt nếu bạn đã từng học qua phương pháp lập trình hướng đối tượng.

Abstract Factory Design Pattern

Abstract Factory là sự mở rộng của tính chất đa hình trong lập trình hướng đối tượng. Mục tiêu hướng đến là có thể tạo ra các đối tượng mà chưa biết trước chính xác kiểu dữ liệu của chúng.

Hãy tưởng tượng, Abstract factory là một nhà máy lớn chứa nhiều nhà máy nhỏ, trong các nhà máy đó có những xưởng sản xuất, các xưởng đó tạo ra những sản phẩm khác nhau. Điều này có vẻ trừu tượng, nhưng đừng vội lo lắng, hãy đi đến cuối nội dung của bài này, và ngẫm lại điều tôi vừa nói, nó sẽ giúp bạn nhớ lâu hơn tư tưởng thiết kế này.

Mục tiêu mà Abstract Factory hướng tới

  • Tạo ra đối tượng mà không cần biết chính xác kiểu dữ liệu.

Tôi giải thích điều này cho các bạn rõ ràng hơn. Ví dụ, bạn sẽ đọc dữ liệu từ một file txt để lấy thông tin tạo đối tượng. Trong file txt đó chứa một số nguyên. Trong mã nguồn bạn viết rằng, nếu dữ liệu đọc được là số 1 thì tạo ra đối tượng A, nếu là 2 thì tạo ra đối tượng B. Vậy rõ ràng, đối tượng của bạn chỉ được tạo ra trong quá trình run time, nghĩa là trong lúc bạn viết mã nguồn chương trình, bạn chưa xác định được đối tượng mình cần tạo. Abstract Factory có thể giúp bạn giải quyết vấn đề này.

  • Giúp mã nguồn của bạn trở nên dễ dàng bảo trì nếu có sự thay đổi.

Trước khi tìm hiểu về điều này, bạn có thể tìm hiểu thêm về Factory Design Pattern, Abstract Factory được hình dung như là một nhà máy chứa nhiều nhà máy con. Mỗi nhà máy con là một factory. Bạn có thể sử dụng Abstract Factory để tạo ra tất cả các đối tượng trong chương trình của bạn. Khi bạn sửa đổi hoặc thêm mới một đối tượng, các đối tượng khác sẽ không bị ảnh hưởng.

Mô tả tư tưởng

Untitled

Sử dụng Asbstract Factory để tạo ra tất cả các đối tượng trong chương trình của bạn.

Hiện thực hóa

Đầu tiên bạn tạo một hệ thống các lớp đối tượng như trên. Bạn cần thực hiện đến cấp Plant. Nghĩa là bạn sẽ tạo ra lớp Grass, Tree, Flower, những lớp này kế thừa từ lớp thuần ảo Plant. Tương tự điều đó với Rabit và Rat. Tôi hi vọng điều này không quá khó với bạn. Nếu gặp rắc rối, hãy xem lại những bài viết về phương pháp lập trình hướng đối tượng trong STDIO.

Sau đó, hiện thực Plant Factory như sau.

Plant* getPlant(int ID)
{
	switch (ID)
	{
	case PLT_GRASS:
		return new Grass();
	case PLT_TREE:
		return new Tree();
	case PLT_FLOWER:
		return new Flower();
	default:
		break;
	}
}

Hiện thực Animal Factory

Animal* getAnimal(int ID)
{
	switch (ID)
	{
	case ANL_RABIT:
		return new Rabit();
	case ANL_RAT:
		return new Rat();
	default:
		break;
	}
}

Hiện thực AbstracFactory

class AbstracFactory
{
public:

	Plant* createPlant(int ID)
	{
		return getPlant(ID);
	}

	Animal* createAnimal(int ID)
	{
		return getAnimal(ID);
	}
};

Một ví dụ để gọi factory trong hàm man().

int main()
{
	AbstracFactory* factory			= new AbstracFactory();

	Plant*			obj1			= factory->createPlant(PLANT::PLT_GRASS);
	Animal*			obj2			= factory->createAnimal(ANIMAL::ANL_RABIT);

	return 0;
}

Một số chú ý

  • Trong code mẫu, thay vì hiện thực các factory con là lớp, tôi đã linh động hiện thực thành các hàm, bởi chúng có thể thực hiện cùng một mục đích, nhưng hàm thì không cần thông qua thể hiện, hay tạo lớp static, vấn đề này liên quan đến hiệu suất chương trình nên tôi xin không đề cập sâu.
  • Với những ngôn ngữ khác như Java, việc đa kế thừa là không thể, nên bạn có thể thay đổi các lớp factory thành các hàm như trên hoặc thành các interface.
  • Bạn có thể kết hợp Singleton Design Pattern cùng với Abstract Factory để làm cho Abstract Factory có thể gọi được ở mọi nơi trong chương trình. Điều này sẽ linh động trong việc tạo ra các thể hiện. Tuy nhiên bạn cần cân nhắc bởi nó sẽ tiêu tốn nhiều tài nguyên để gọi hàm.

Demo

Tham khảo demo tại đây.

Lời tri ân

Bài viết là lời tri ân của tôi gửi đến tất cả các trainer của STDIO Training, lời cảm ơn chân thành đến tất cả những con người đã hết lòng cống hiên tri thức cho xã hội. Chúc tất cả mọi người luôn thành công và tiếp tục cống hiến.

THẢO LUẬN
ĐÓNG