Hôm nay chúng ta sẽ được hướng dẫn về cách sử dụng cũng như công dụng của 2 Pattern khá phổ biến Abstract Factory & Factory Method

1. Abstract Factory

Phương châm của Abstract Factory khá rõ ràng như tên gọi của nó: Thay vì dùng toán tử new để tạo mới cho từng loại class cụ thể, hãy define một interface hoặc một abstract class dùng để tạo ra những class có quan hệ với nhau, đó có thể là quan hệ kế thừa (inheritance) hoặc quan hệ phụ thuộc (dependency) với nhau. Vậy chúng ta sẽ chỉ có một class hoặc một interface dùng để một nhóm các class (factory of class).

Lợi ích

Với Abstract Factory các lập trình viên có thể cùng nhau làm việc trên một nhóm các đối tượng trong một cụm chức năng có liên quan với nhau, có thể khai báo tạo mới, chỉnh sửa thông tin nhưng vẫn giữ được sự nhất quán cao trong code.

Khi nào nên sử dụng:

  • Tách biệt code quản lý thông tin, khai báo tạo mới cho từng class với code của các class dùng để xử lý thông tin khác. Ví dụ: chúng ta sẽ có một package (hay một sub project common) chỉ dùng để quản lý tất cả thông tin của tất cả các class Entity, Model, ... và interface cho các tầng khác sử dụng chúng. Khi đó một vài lập trình viên chịu trách nhiệm định nghĩa tất cả những thông tin khai báo này, còn những người khác có thể tập trung vào xây dựng logic cho chức năng mà không cần quan tâm nhiều đến những thông tin chi tiết của từng class. 
  • Khi chức năng của Hệ thống yêu cầu nhiều class với các thông tin có quan hệ nhất quán với nhau. Ví dụ: chức năng tính lương cho nhân viên, hoặc in báo cáo tháng - quý - năm , ...

 

Sơ đồ UML:

 

Code sample:

// The abstract factory interface declares a set of methods that
// return different abstract products. These products are called
// a family and are related by a high-level theme or concept.
// Products of one family are usually able to collaborate among
// themselves. A family of products may have several variants,
// but the products of one variant are incompatible with the
// products of another variant.
interface GUIFactory is
    method createButton():Button
    method createCheckbox():Checkbox


// Concrete factories produce a family of products that belong
// to a single variant. The factory guarantees that the
// resulting products are compatible. Signatures of the concrete
// factory's methods return an abstract product, while inside
// the method a concrete product is instantiated.
class WinFactory implements GUIFactory is
    method createButton():Button is
        return new WinButton()
    method createCheckbox():Checkbox is
        return new WinCheckbox()

// Each concrete factory has a corresponding product variant.
class MacFactory implements GUIFactory is
    method createButton():Button is
        return new MacButton()
    method createCheckbox():Checkbox is
        return new MacCheckbox()


// Each distinct product of a product family should have a base
// interface. All variants of the product must implement this
// interface.
interface Button is
    method paint()

// Concrete products are created by corresponding concrete
// factories.
class WinButton implements Button is
    method paint() is
        // Render a button in Windows style.

class MacButton implements Button is
    method paint() is
        // Render a button in macOS style.

// Here's the base interface of another product. All products
// can interact with each other, but proper interaction is
// possible only between products of the same concrete variant.
interface Checkbox is
    method paint()

class WinCheckbox implements Checkbox is
    method paint() is
        // Render a checkbox in Windows style.

class MacCheckbox implements Checkbox is
    method paint() is
        // Render a checkbox in macOS style.


// The client code works with factories and products only
// through abstract types: GUIFactory, Button and Checkbox. This
// lets you pass any factory or product subclass to the client
// code without breaking it.
class Application is
    private field button: Button
    constructor Application(factory: GUIFactory) is
        this.factory = factory
    method createUI() is
        this.button = factory.createButton()
    method paint() is
        button.paint()


// The application picks the factory type depending on the
// current configuration or environment settings and creates it
// at runtime (usually at the initialization stage).
class ApplicationConfigurator is
    method main() is
        config = readApplicationConfigFile()

        if (config.OS == "Windows") then
            factory = new WinFactory()
        else if (config.OS == "Mac") then
            factory = new MacFactory()
        else
            throw new Exception("Error! Unknown operating system.")

        Application app = new Application(factory)

 

Điểm không lợi:

Để có thể tạo ra được cấu trúc code theo chuẩn pattern Abstract Factory, chúng ta sẽ cần tạo ra khá nhiều interface và các class con kế thừa. Như vậy với những chức năng đơn giản cấu trúc code của chúng ta sẽ trở nên phức tạp hơn rẩt nhiều lần so với việc không áp dụng chuẩn Abstract Factory.

Qua đây có thể thấy rằng không phải lúc nào chúng ta cũng phải chăm chăm áp dụng Design Pattern vào dự án, hay source code của mình. Tất cả mọi sản phẩm làm ra đều phải có giá trị riêng và lợi ích của nó, sản phẩm phần mềm trước nhất phải phục vụ người dùng với đúng chức năng của nó là quan trọng nhất. Với những bạn chưa có nhiều kinh nghiệm thì phải trải qua ít nhiều vài ba dự án, làm ra sản phẩm và nhận được sư phản hồi của khách hàng về chính sản phẩm của mình hoặc phải trải qua những giai đoạn bảo trì code,... mới có thể dần hiểu được những giá trị mà Design Pattern muốn truyền tải.

Chúc các bạn thành công.

AutoCode.VN

minhnhatict@gmail.com Creational Pattern