Nội dung bài viết
Video học lập trình mỗi ngày
Factory pattern là gì? Trước khi đi vào định nghĩa thì chúng ta đi xem xét một ví dụ thực tế trong đời sống hằng ngày của chúng ta. Đó là việc đi mua xe ô tô của tôi diễn ra như thế nào? Và chúng ta sẽ lấy việc mua ô tô ra làm ví dụ và sử dụng Factory pattern để mô phỏng quá trình này. Và từ đó chúng ta sẽ có một kết luận cho riêng chúng ta. Bắt đầu. Trước khi đi vào bào viết này thì lời khuyên của tôi là bạn nên hiểu và biết, không cần sâu về 9 mẫu thiết kế trong lập trình hướng đối tượng mà bất cứ developers nào cũng phải học.
Bởi vì sao, bời vì mỗi dự án của mỗi công ty khác nhau, do đó họ thiết kế của họ khác nhau, nhưng cũng nằm trong 9 mẫu này mà thôi. Biết được để join vào một cách nhanh chóng hơn. Và bài trước, tôi đã trình bày về mẫu thiết kế Bulder pattern. Bạn có thể mất 2 phút để đọc bài viết đó, trước khi qua lại bài viết này. Một vài điều chú ý nhé.
Factory pattern là gì?
Factory pattern là một trong 9 Pattern phổ biến trong lập trình hướng đối tượng (OOP). Nó giúp cho chúng ta định nghĩa được nhiều đối tượng và cho phép các lớp con tự quyết định là cái nào được khởi tạo. Đỡ mất thời gian cho việc định nghĩa này thì tôi tóm tắt nhanh và đi vào những ví dụ cụ thể.
Theo nghĩa tiếng việt là gì? Chúng ta gọi là một hình nhà máy. Mà nhà máy là gì? là một nới sản xuất những sản phẩm như sữa, ô tô, xe đạp... Hoặc sản xuất những sản phẩm thiết yếu cho đời sống hằng ngày chúng ta. Trong 9 mẫu thiết kế thì có một mẫu thiết kế được gọi là factory pattern, cung cấp cách tốt nhất để tạo ra các đối tượng. Và quan trọng nó chia ra 3 loại mô hình đó là:
- Simple factory
- Factory method
- Abstract factory
Bình tĩnh, mọi người đã nghe về 3 loại này nhưng chưa chắc là hiểu. Và hôm nay, bạn sẽ hiểu, chắc luôn. Chúng ta sẽ đi từ từ phân tích và xem xét những ví dụ về cách mua xe ô tô cho chúng ta.
Simple factory là gì?
Simple factory hay còn gọi là phương thức nhà máy tĩnh( Static Factory Methods) dịch ra tiếng việt đọc nó vô duyên thấy ớn. Vậy Phương thức nhà máy tĩnh là gì? Nó là một phương thức tĩnh công khai trên đối tượng trả về một thể hiện mới của đối tượng. Khó hiểu vãi nhỉ, thôi để lấy ví dụ cho khoẻ. Cụ thể định nghĩa này tương đương với tình huống thực tế sau đây.
Mô hình này dành cho những người dùng không quan tâm đến thông số của sản phẩm hay không cần biết chi tiết sản xuất cụ thể của sản phẩm, mà chỉ quan tâm là tiêu thụ sản phẩm mà thôi. Vi dụ bạn nghe nói nhiều về KIA Seltos, bạn thấy nó ok không quan tâm nó được sản xuất thế nào, thì bạn mua thôi. Vì thế mô hình này áp dụng Simple factory để sản xuất cho bạn, vì nó thật sự đơn giản.
Trên hình vẽ là một ví dụ cho các bạn dễ hình dung. Nhà máy này có hai bộ phận sản xuất tương ứng với hai mẫu ô tô. CR7 và M10 lần lượt đặt mua các mẫu xe LUX A2.0 và LUX SA2.0 từ nhà máy VINFAST. sau đó nhà máy sẽ xác định mẫu xe do người dùng lựa chọn, sau đó sản xuất theo mẫu xe tương ứng Và được chuyển đến tay người dùng sau khi sản xuất xong. Hãy cùng xem cách sử dụng nhà máy đơn giản để mô tả quy trình sản xuất các mẫu ô tô cụ thể trong nhà máy VINFAST như thế nào?
"use strict"; class VIN { } class LUXA20 extends VIN { run() { console.log("LUXA20 Ô tô"); } } class LUXSA20 extends VIN { run() { console.log("LUXSA20 Ô tô"); } } class VINFactory { static produceVIN(model) { if (model === "A.20") { return new LUXA20(); } else { return new LUXSA20(); } } }
Trong đoạn code trên, chúng ta định nghĩa một class có tên là VINFactory, class này cung cấp một phương thức tĩnh là produceVIN() để tạo các loại ô tô khác nhau tùy thuộc vào các thông số của mô hình. Cách sử dụng như sau:
const luxA20 = VINFactory.produceVIN("A.20"); const luxSA20 = VINFactory.produceVIN("SA.20"); luxA20.run(); luxSA20.run(); //Ouputs: [LOG]: "LUXA20 Ô tô" [LOG]: "LUXSA20 Ô tô"
Một ví dụ rất đơn giản và trực quan.
Như vậy thông qua trường hợp trên chúng ta có thể đúc kết ra những tình huống được ứng dụng như sau:
Lớp factory chịu trách nhiệm tạo ít đối tượng hơn: Vì có ít đối tượng được tạo hơn nên logic nghiệp vụ trong phương thức factory sẽ không quá phức tạp. Máy khách chỉ cần biết các tham số được truyền vào phương thức tĩnh của lớp factory, và không cần quan tâm đến chi tiết của việc tạo đối tượng.
Bây giờ chúng ta qua loại thứ 2 đó là Factory method. Tiếp tục đừng dừng ở đây.
Factory Method Pattern là gì?
Factory method là một pattern nằm trong nhóm Polymorphic Factory (đa hình). Và vì tính trừu tượng của nó, Factory Method còn được gọi là Virtual Constructor. Factory Method giải quyết vấn đề khởi tạo đối tượng mà không chỉ ra cụ thể chính xác lớp nào sẽ khởi tạo, ủy quyền cho lớp con.
Ở ví dụ đầu tiên thì nhà máy VIN đã biết được thằng CR7 và thằng M10 đặt xe gì để mà sản xuất. Nghĩa là biết trước rồi, nên áp dụng mô hình Simple factory. Nhưng ở đây là khác, khi mà hai thằng đó nó không đặt trước, cho nên Phạm Nhật Vượng không biết sản xuất thế nào. Chình vì thế nhà máy VINFAST quyết định sản xuất luôn cả hai xe (LUX A20, SA20) một lúc với số lượng nhất định.
Ở đây tôi cần bạn phải hiểu và tinh ý một chút về hai loại hình này. Tôi tiếp tục vẽ thêm môtj ví dụ khác về mô hình này. Ta liên tưởng đến tính đa hình và trừu tượng trong lập trình hướng đối tượng. Khi bạn có một lớp cha nhưng không rõ chính xác lớp con nào sẽ được yêu cầu trong quá trình thực thi. Trong thiết kế kiến trúc phần mềm, ta có một mẫu thiết kế liên quan đến tính chất này và giải quyết những vấn đề đó, Factory Method Pattern. Trong hình trên, tôi mô phỏng lại quá trình mua xe của CR7 mà M10. Hai thằng có tiền nên thích mua gì kệ nó. Nó đến showroom xem xét, rồi thích thì mua thôi. Dân có tiền hay chơi vậy. Bởi vậy cho nên phải có 2 loại xe sẵn đó cho nó.
Vậy Code làm sao đây. Đây, tiếp tục mô hình.
class VIN { } class LUXA20 extends VIN { run() { console.log("LUXA20 Ô tô"); } } class LUXSA20 extends VIN { run() { console.log("LUXSA20 Ô tô"); } } class VINFactory { } class LUXA20Factory extends VINFactory { produceVIN() { return new LUXA20(); } } class LUXSA20Factory extends VINFactory { produceVIN() { return new LUXSA20(); } }
Cách sử dụng, và sẵn sàng sản xuất ngay khi các VIP yêu cầu mua xe.
const luxa20Factory = new LUXA20Factory(); const luxsa20Factory = new LUXA20Factory(); const luxa20 = luxa20Factory.produceVIN(); const luxsa20 = luxsa20Factory.produceVIN(); luxa20.run(); luxsa20.run();
Ông nào không hiểu thì đọc lại mấy lần và run code để hiểu nhé. Tiếp đi vào loại thứ 3, mệt quá.
Abstract factory là gì?
Tương tự như Factory Method Pattern, Abstract Factory Pattern cũng là một design pattern thuộc nhóm khởi tạo (creational patterns) tuy nhiên nó cao cấp hơn Factory Method Pattern ở chỗ nó cho phép tạo ra một super factory dùng để tạo ra các factory khác. Để dễ hình dung hơn, tôi tiếp tục lấy nhà sản xuất xe VINFAST là làm ví dụ tiếp. Ở hai ví dụ trên thì, mỗi bộ phận chịu trách nhiệm sản xuất một chiếc xe. Cụ thể LUXA20Factory nhiệm vụ sản xuất xe LUX A2.0, tương tự như vậy cho LUXSA20Factory.
Nhưng điều quan trọng là hai xe này lại có chung khung, gầm, lốp. Như vậy nếu để vậy thì sẽ phải tốn nhân công và công sức không? Hay chuyển về một bộ phận làm chung những cái đó??? Abstract Factory như 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.
Và rất may trong trường hợp này, Abstract Factory Pattern chính là chiếc phao có thể giải nguy cho bạn trong trường hợp này. Điều đầu tiên mà Abstract Factory Pattern đề xuất chính là việc khai báo từng interface cho từng sản phẩm riêng biệt trong một nhóm sản phẩm như khung, gầm lốp cho hai loại xe. Và sau đó Sau đó, mỗi loại biến thể của từng sản phẩm riêng biệt sẽ phải tiến hành implements interface đó mà thôi. Để hiểu về Abstract thì theo tôi các bạn mới học khó có thể tiếp thu được đâu.
Cho nên các bạn chỉ nên học hai ví dụ trên mà thôi. Còn ví dụ này đọc cho biết, chứ áp dụng quả là một vấn đề. Nhưng áp dụng đươc, thì bạn quả là một lập trình viên giỏi. Tôi nói thật đấy. Bạn rất giỏi và am hiểu nghiệp vụ.
Code triển khai mô hình trên
abstract class VIN { abstract run(): void; } class LUXA20 extends VIN { run(): void { console.log("LUXA20 Ô tô"); } } class LUXSA20 extends VIN { run(): void { console.log("LUXSA20 Ô tô"); } } abstract class VINFactory { abstract produceLUXA20(): LUXA20; abstract produceLUXSA20(): LUXSA20; } class ConcreteVINFactory extends VINFactory { produceLUXA20(): LUXA20 { return new LUXA20(); } produceLUXSA20(): LUXSA20 { return new LUXSA20(); } }
Cách sử dụng như sau:
const vinFactory = new ConcreteVINFactory(); const luxA20 = vinFactory.produceLUXA20(); const luxSA20 = vinFactory.produceLUXSA20(); luxA20.run(); luxSA20.run();
Các tình huống ứng dụng về mô hình Abstract Factory bạn có thể chú ý những điều sau đây:
- Bất cứ hệ thống nào cũng không nên phụ thuộc vào một lớp. Hay nói cách khác là. Một hệ thống không nên phụ thuộc vào các chi tiết về cách các cá thể lớp sản phẩm được tạo, kết hợp và thể hiện, điều này rất quan trọng đối với tất cả các loại mẫu của nhà máy.
- Hệ thống cung cấp một thư viện sản phẩm, tất cả các sản phẩm xuất hiện với cùng một giao diện, để khách hàng không phụ thuộc vào việc thực hiện cụ thể. Đó là diều bạn lưu ý.
Lời kết
Trên đây là tất cả những gì tôi muốn truyền đạt lại cho các bạn thông qua những ví dụ thực tế về mô hình nhà máy (Factory Pattern). Là một trong 9 mẫu thiết kế mà bất cứ mỗi dev nào cũng phải học và áp dụng. Ở bài trước chúng ta đã nói về mẫu thiết kế Builder pattern. Bạn có thể xem lại bài trước để thêm một sự hiểu biết cho tương lai của mình.
Và sự thật đó là tôi không làm cho VINFAST, chẳng qua nói xạo để thu hút bạn đọc mà thôi kakakaka.