November 13, 2011

PHP - Design Pattern - Multiton & Factory

Tiếp nối chuỗi bài Design Pattern bằng ngôn ngữ PHP, hôm nay mình giới thiệu thêm 2 mẫu thiết kế cũng rất phổ biến và thông dụng, đó là mẫu Multiton và mẫu Factory.
Mẫu Multiton được mở rộng từ mẫu singleton. Nếu bạn nào để ý sẽ phát hiện ngay trong mẫu singleton phần contructor hoàn toàn không có tham số trong đó. Nhưng trong rất nhiều trường hợp, chúng ta cần khởi tạo một đối tượng với những thông số cấu hình nhất định, và cũng chính vì điều này mà multiton ra đời.

Multiton
<?php
class Multiton
{
    private static $_instances = array();
    private static $_key;
    private $_opt1;
    private $_opt2;
    protected function __construct($param1, $param2)
    {
        $this->_opt1 = $param1;
        $this->_opt2 = $param2;
    }
    public static function getInstance($param1, $param2)
    {
        self::$_key = $param1 . $param2;
        if(!self::$_instance[self::$_key] instanceof self)
            self::$_instance[self::$_key] = new self($param1, $param2);
        return self::$_instance[self::$_key];
    }
}
Mẫu factory nhằm giúp hạn chế những câu lệnh if...else hoặc switch ... case vốn ngốn rất nhiều resource của hệ thống. Với mẫu này. chúng ta sẽ sử dụng 1 đối tượng trung gian để khởi tạo đúng đối tượng mà không cấn thông qua các câu lệnh if...else hoặc switch...case.

Lớp đối tượng chính.
<?php
class ObjMain
{
    private static $_instance;
    protected function __construct(){}
    public static function getInstance()
    {
        if (!self::$_instance instanceof self)
            self::$_instance = new self();
        return self::$_instance;
    }
}

Lớp đối tượng trung gian
<?php
class ObjFactory
{
    public static function factory($className)
    {
         return $className::getInstance();
    }
}
Cách sử dụng
<?php
$obj = ObjFactory::factory('ObjMain');

PHP - Design Patterns - Singleton & Registry

8 comments:

  1. Minh cung chua that su hieu lam tac dung va loi ich cua mau factory? Mong ban giai thich them duoc ko vay?

    ReplyDelete
  2. Chào bạn,
    Mẫu factory nhằm mục đích lấy ra 1 đối tượng thuộc 1 nhóm đối tượng có cùng 1 cấu trúc tên class.
    Ví dụ:
    class Dao_MySQL extends Dao_Abstract {...}
    class Dao_Oracle extends Dao_Abstract {...}
    class Dao_Db2 extends Dao_Abstract {...}

    Nếu bạn muốn tạo 1 đối tượng từ 1 class nào đó thì rất đơn giản. Cụ thể
    $mysql = new Dao_MySQL(); // Dùng new
    hay
    $mysql = Dao_MySQL::getInstance(); // Dùng singleton

    Nhưng nếu bạn muốn khởi tạo đối tượng này thông qua cấu hình chứ không phải là hard-code như trên thì dùng Factory là lựa chọn.
    Ví dụ:
    $config = 'MySQL'; // Config lưu 1 chỗ khác
    $objDao = Dao_Factory($config);

    Lúc này $obj chính là Dao_Mysql
    Thanks

    ReplyDelete
    Replies
    1. Cám ơn câu trả lời của bạn. Nhưng mình thấy với những ngôn ngữ biên dịch(c#,java....) thì dùng file config mới có tác dụng ko cần biên dịch lại, còn với php ngôn ngữ dạng script thì đâu cần thiết lắm đâu.

      Delete
    2. Bạn có thể cho mình biết 1 ví dụ cụ thể là tại sao cách dùng

      $config = 'MySQL'; // Config lưu 1 chỗ khác
      $objDao = Dao_Factory($config);

      dùng tốt hơn cách dùng

      $mysql = new Dao_MySQL();

      trong trường hợp cụ thể nào không. ( Bỏ qua trường hợp hard-code bạn nêu trên)

      Ps: sorry mình hỏi hơi nhiều tại vì mình hiểu lơ mơ quá. Mong nhận sự giúp đỡ của bạn.

      Delete
    3. Hi bạn,
      Không gì cả, thêm bạn thêm vui. Thêm comment, blog mình thêm sôi động. Rất cảm ơn bạn đã để lại lời nhắn, nhưng không biết bạn có thể cho mình biết tên để tiện trao đổi :)
      Giải thích cho bạn như sau:
      1 - Tại sao phải lưu config mà không hard-code trực tiếp.
      Bên trên là VD cho việc tương tác DB, vậy mình lấy luôn ví dụ này để nói cho rõ luôn.
      Thường thì bạn tương tác PHP - DB thì 99% là nghĩ sẽ thao tác với MySQL. Nhưng biết đâu 1 ngày đẹp trời nào đó bạn sẽ làm việc với Oracle, Db2, MsSQL...
      Như vậy, nếu tại tầng code của bạn hard-code là "new Dao_MySQL" thì khi bạn chuyển đổi bạn phải đi dò từng chỗ vào sửa code. Cực khổ
      Như vậy bạn dùng Factory để triển khai thì khi bạn chuyển DBMS khác, bạn chỉ cần vào config sửa lại cái bạn muốn xử lý, viết thêm 1 class tương ứng là xong.
      Bạn sẽ thắc mắc, vậy cái class mới của tôi chắc gì giống với cái class cũ mà dùng đc. Việc này thì bạn chỉ cần cho class mới kế thừa 1 abstract class hoặc implements 1 interface chung của các class DAO

      Delete
    4. 2 - Dùng new trực tiếp và factory có lợi gì?
      Trả lời cho câu hỏi này, thì phần số 1 có lẽ cũng giúp bạn thấy ít nhiều phần lợi của factory. Đó là cơ chế mở rộng. Một project viết hôm nay, nhưng ngày mai nó sẽ có thêm 1 số feature khác. Factory giúp bạn linh hoạt hơn.

      Nhưng về performance thì dĩ nhiên bạn new trực tiếp nó nhanh hơn rồi. Nhưng nếu bạn cứ if...else hay switch...case để khởi tạo đúng class/object thì nó còn chậm hơn

      Delete
  3. anh có thể viết thêm loạt bài hướng dẫn về design patterns k ạ. e thấy cách tóm tắt của a rất là dễ hiểu ạ. e cảm ơn hì hì

    ReplyDelete
    Replies
    1. Cảm ơn em vì đã thích những bài hướng dẫn của anh. Cũng lâu rồi anh không có viết những bài viết về đề tài này nữa, hy vọng ngày nào đó lại tiếp tục viết tiếp để em xem ^_^

      Delete