Factory Design pattern


Hello các bạn, hôm nay mình sẽ tiếp tục series Design pattern thông dụng với Factory Pattern. Đây là một pattern mà ẩn sau nó có khá nhiều nguyên lý thiết kế phần mềm.
Tình huống
Giả sử bạn vào nhà hàng bán pizza, trong cửa hàng có nhiều loại pizza khác nhau như là pizza pho mát hoặc pizza dứa, các loại pizza này có chung các công đoạn như là chuẩn bị nguyên liệu, nướng, đóng hộp mặc dù cách làm cụ thể là khác nhau. Khi bạn đặt hàng 1 chiếc pizza, bạn chỉ cần báo nhà hàng pizza loại gì thì sau một lúc bạn sẽ nhận được chiếc bánh pizza thơm ngon theo yêu cầu của bạn mà không cần biết các công đoạn chế biến chiếc bánh.
Có lẽ lúc đầu bạn sẽ thiết kế các class như sau:
interface IPizza
các concrete class triển khai IPizza
class PizzaStore với method OrderPizza().
interface IPizza và các concrete class triển khai IPizza
class PizzaStore với method OrderPizza()
Tuy nhiên
Giả sử nhà hàng sản xuất thêm nhiều loại pizza khác nữa thì bạn phải thêm nhiều điều kiện if else trong hàm OrderPizza(). Đây là một thiết kế không tốt vì bạn sẽ liên tục phải sửa lại code cũ. Code tốt nên hạn chế việc sửa chữa và ưu tiên việc mở rộng. Phần tạo mới pizza là phần dễ thay đổi, những phần dễ thay đổi nên được gom lại để dễ dàng sửa chữa và quản lý, hạn chế thay đổi các phần khác.
Vậy là rõ ràng, ta sẽ tách phần khởi tạo pizza ra ngoài hàm OrderPizza, đặt chúng vào 1 class là Factory, toàn bộ logic khởi tạo Pizza sẽ được xử lí trong Factory:
class PizzaFatory chịu trách nhiệm tạo object
class PizzaStore có ref tới PizzaFatory nhằm để tạo các object Pizza.
class PizzaFatory chịu trách nhiệm tạo object
class PizzaStore có tham chiếu tới PizzaFatory nhằm để tạo các object Pizza
Định nghĩa
Ví dụ trên chính là về ý tưởng của Factory Pattern. Bằng việc đẩy logic khởi tạo vào một class, ta có thể dễ dàng sửa chữa và thay đổi code phần đó mà không ảnh hưởng đến phần khác. Tuy nhiên đây mới chỉ là Simple Factory pattern, là tiền đề cho Factory Method Pattern và Abstract Factory pattern.
Simple Factory có một số nguyên lý thiết kế phầm mềm ta có thể rút ra được là:
Gom những thứ dễ thay đổi vào một chỗ. Điều này giúp cho việc quản lý sự thay đổi dễ dàng hơn mà không ảnh hưởng tới phần khác.
Tận dụng sức mạnh của composition thông qua class Factory.
Code nên hạn chế sửa, thay vào đó ưu tiên việc mở rộng.
Tiếp tục ví dụ
Hãy tiếp tục tiếp ví dụ về nhà hàng pizza. Giả sử nhà hàng phát triển và có thêm nhiều cơ sở khác nhau như cơ sở Hà Nội và cơ sở Hồ Chí Minh, mỗi cửa hàng lại có thêm các cách trang trí khác nhau, dù là cùng loại pizza dứa hay pho mát (do khẩu vị vùng miền khác nhau). Tới đây thì việc tạo bánh pizza nào sẽ tương đối phức tạp nếu ta để hết logic trong PizzaFactory. Lúc này bạn có thể nghĩ đến việc tạo nhiều factory khác nhau như là HCMPizzaFactory hoặc là HNPizzaFactory, các Factory sẽ được tham chiếu bởi PizzaStore. Simple Factory chỉ giải quyết bài toán tạo 1 loại object chứ không có biến thể cho từng loại object. Tất nhiên bạn có thể thêm và sửa toàn bộ logic trong Factory, tuy nhiên để quá nhiều logic phức tạp trong 1 Simple Factory là điều hạn chế, hơn nữa tạo nhiều Factory sẽ khiến code trùng lặp và khó bảo trì hơn.
Giải pháp
Đến lúc này bạn có 2 cách.
Cách 1: bạn hoàn toàn có thể bỏ đi Factory, thay vào đó chỉ cần sử dụng method CreatPizza() và các class kế thừa PizzaStore. Việc tạo pizza như thế nào hoàn toàn do các subclass của PizzaStore xử lí. Đây chính là Factory Method.
Cách 2: bạn tạo một interface IFactory và cho các subclass Factory quyết định việc tạo object. Chúc mừng bạn, đây chính là cách tiếp cận Abstract Factory. (sẽ nói kĩ hơn ở phần 3)
Factory method
Ta sẽ thiết kế PizzaStore thành abstract, bên trong nó có 1 phương thức abstract createPizza nhằm để tạo object, logic khởi tạo cụ thể sẽ được triển khai tại các class con kế thừa PizzaStore. Bằng việc đẩy logic khởi tạo xuống lớp con, ta hoàn toàn có thể custom logic từng trường hợp, và để cho lớp con tự xử lí logic của nó. Cách này sử dụng tính đa hình và kế thừa của OOP.
Refactor code sử dụng Factory Pattern
Định nghĩa
Tóm lại, Factory method có 5 thành phần chính:
Creator class: là abstract class, định nghĩa 1 phương thức abstract factory nhằm để tạo object. Ví dụ ở trên thì là PizzaStore
createProduct(): là phương thức abstract factory nhằm để tạo object, được triển khai cụ thể tại các ConcreteCreator. ở ví dụ trên đó chính là CreatePizza()
ConcreteCreator: các lớp cụ thể kế thừa Creator
Product class: Là type chung của object được tạo từ phương thức createProduct().
ConcretProduct: type cụ thể của object được tạo ra từ ConcreteCreator
Thiết kế của Factory Pattern
Vậy khi nào thì dùng factory pattern:
Khi bạn không biết trước chính xác các loại và sự phụ thuộc của các object mà code của bạn cần làm việc với. Ví dụ bạn có một Order có thể thêm các Product, các Product có thể tồn tại mã giảm giá, việc sử dụng Factory Method trong tình huống này có thể xử lí được việc tạo objet
Khi bạn đang phát triển thư viện hoặc framework, bạn muốn cung cấp cho người dùng 1 phương tiện để có thể extends các class bên trong, tăng sự linh hoạt cho code. Ví dụ bạn có 1 UI framework trong đó có 1 class Button, bạn sẽ cung cấp abstract method cho người dùng để có thể tạo ra 1 RoundButton dựa trên Button của bạn.
Khi bạn muốn tiết kiệm tài nguyên hệ thống bằng cách tái sử dụng các đối tượng đã tồn tại thay vì tạo lại chúng mỗi lần, bạn có thể áp dụng Factory Method. Điều này giúp giảm tải cho hệ thống và cải thiện hiệu suất bằng cách tránh việc khởi tạo lại các đối tượng phức tạp và tốn kém.
So sánh với Simple Factory
Simple Factory không phải là design pattern, nó là programming idiom, còn Factory method là pattern.
Simple factory tạo object thông qua composition, còn factory method tạo object từ abstract method sử dụng kế thừa và đa hình
Simple factory đơn giản hơn so với factory method, nếu bạn cần tạo nhiều biến thể của Product, xem xét sử dụng factory method
Lưu ý
Creator class phải là abstract hoặc là interface và phải có phương thức abstract factory method
hàm khởi tạo phải được gọi trong 1 hàm khác ở Creator, nếu Creator chỉ tồn tại 1 method thởi tạo thì nó không thể hiện bản chất của Factory method
Subscribe to my newsletter
Read articles from Huy Nguyen directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Huy Nguyen
Huy Nguyen
I am a software engineer with 4 years of experience in developing web applications. My expertise lies in backend development, and I have a deep interest in problem-solving, algorithms, system design, and databases. I am always eager to learn and embrace challenging projects, striving to deliver applications that exceed user expectations. I also love sharing my knowledge and learning from others to foster mutual growth and improvement