Design pattern trong PHP (phần 1)

Design pattern là gì ?

Design Pattern là bộ môn thuộc về ngành khoa học máy tính chuyên nghiên cứu các kiên trúc phần mềm. Hiện nay tất cả các Framwork như Codeigniter, Zend, Laravel, ... đều có sử dụng nhữ kiến trúc design pattern có sẵn và mỗi Framwork sẽ có những kiểu design partern riêng. Design Pattern sử dụng nền tảng của lập trình hướng đối tượng, áp dụng các tính chất như tính kế thừa, hàm khởi tạo, tính đa hình, ... để làm nên những kiến trúc phần mềm đáp ứng cho project của họ.

Một ví dụ điển hình về design pattern và được biết đến nhiều nhất tron PHP là cấu trúc MVC

Ngoài ra còn một số pattern sau chúng ta sẽ từ từ tìm hiểu 😄

Danh sách các mẫu design pattern

Tham khảo từ wikipedia

Các mẫu tạo lập

(creational patterns)

  • Abstract factory pattern
  • Builder
  • Factory method pattern
  • Prototype
  • Singleton

Các mẫu cấu trúc

(structural patterns)

  • Adapter
  • Bridge
  • Composite
  • Decorator
  • Facade
  • Flyweight
  • Proxy

Các mẫu ứng xử

(behavioral patterns)

  • Chain of responsibility
  • Command
  • Interpreter
  • Iterator
  • Mediator
  • Memento
  • Observer
  • State
  • Strategy
  • Template method
  • Visitor

Dĩ nhiên danh sách trên là chưa đầy đủ, nhưng ở bài viết này, mình sẽ chỉ giới thiệu về các pattern được ứng dụng nhiều trong PHP cũng như các ngôn ngữ OOP khác.

Factory Pattern

Mẫu này được chia làm 2 loại là Factory Method và Abtract Factory. Vậy sự khác nhau giữa 2 pattern này là gì ? Hãy cùng tìm hiểu qua 2 ví dụ để tìm ra sự khác biệt.

1. Factory method

Định nghĩa

Bản chất của mẫu thiết kế Factory là "Định nghĩa một giao diện (interface) cho việc tạo một đối tượng, nhưng để các lớp con quyết định lớp nào sẽ được tạo. "Factory method" giao việc khởi tạo một đối tượng cụ thể cho lớp con."

Gỉa sử, chúng ta xây dựng 1 "nhà máy" (factory) để "sản xuất" ra các hình vẽ khác nhau, hãy xem mô hình UML dưới đây. factory_pattern_uml_diagram.jpg

Có hiểu đơn giản, chúng ta có 1 nhà máy (ở đây là ShapeFactory), và cần nhà máy này sản xuất ra các hình vẽ theo yêu cầu, mà không cần quan nó sẽ dùng tâm phương pháp hay cách thức nào để tạo ra hình vẽ đó. Đây cũng chính là mục đích của việc sử dụng Factory Method.

Cách thực hiện

Vậy để triển khai mẫu Factory Method phải làm như thế nào ?

  • Đầu tiên chúng ta cần tạo ra 1 interface(hoặc abstract class) ở đây tôi đặt tên là Shape, trong đó có phương thức draw() đây là phương thức để vẽ các hình.
  • Tiếp theo là tạo ra các subclass, ví dụ tạo class Circle kế thừa class/ hoặc implements interface Shape, class này sẽ override lại phương thức draw() của Shape đã tạo để vẽ ra hình tròn.
  • Tiếp theo chúng ta tạo class Square cũng kế thừa Shape, class này sẽ định nghĩa lại phương thức draw() để vẽ ra hình vuông.
  • Tương tự với class Rectangle để tạo ra hình chữ nhật.
  • Và quan trọng nhất là thực hiện Factory Method bằng cách tạo ra class ShapeFactory, tôi sẽ nói rõ hơn ở phần dưới đây.

Ví dụ

Trước hết chúng ta tạo file Shape.php với nội dung như sau

<?php
interface Shape {
    // Có thể định nghĩa sẵn const hoặc không :D
    const SQUARE = 1;
    const CIRCLE = 2;
    const RECTANGLE = 3;
    // general method
    function draw();
}

Tiếp theo là tạo 3 class implements interface Shape

<?php
class Circle implements Shape
{
    public function draw() {
        // thực hiện code vẽ hình tròn tại đây
        echo "Draw circle";
    }
}
<?php
class Square implements Shape
{
    public function draw() {
        // thực hiện code vẽ hình vuông tại đây
        echo "Draw square";
    }
}
<?php
class Rectangle implements Shape
{
    public function draw() {
        // thực hiện code vẽ hình chữ nhật tại đây
        echo "Draw rectangle";
    }
}

Cuối cùng để thực hiện Factory Method, chúng ta tạo ra lớp ShapeFactory như dưới đây. Class này sẽ tạo ra các đối tượng cụ thể tại thời điểm thực thi (at run time)

<?php
class ShapeFactory
{
    public function getShape($type) {
        switch ($type) {
            case Shape::SQUARE:
                return new Square;
                break;
            case Shape::CIRCLE:
                return new Circle;
                break;

            case Shape::RECTANGLE:
                return new Rectangle;
                break;
            default:
                return null
                break;
        }
        return null;
    }
}

getShape chính là factory method, với tham số truyền vào là kiểu hình vẽ muốn khởi tạo. Đây chính là mấu chốt của việc thực hiện pattern này .

Và cuối cùng, chúng ta sẽ tạo ra các đối tượng cụ thể, hay các hình vẽ như sau.

<?php
$factory = new ShapeFactory();
// hình tròn Shape::CIRCLE
$shapeCircle = $factory->getShape(Shape::CIRCLE);
$shapeCircle->draw();

=> in ra màn hình Draw circle

Tương tự, vẽ các hình khác chỉ cần thay đổi giá trị của $type trong getShape($type) thành định danh của hình vuống hay chữ nhật ta sẽ lấy được object mong muốn.

Như vậy chúng ta ó thể thấy rõ ràng là chúng ta không cần biết phương thức draw kia thực hiện bằng cách nào mà chỉ cần yêu cầu từ 'factory', nhiệm vụ còn lại là factory sẽ phải cung cấp chính xác điều mà chúng ta muốn.

2. Abstract Factory Pattern

Định nghĩa

Là thiết kế mẫu hướng đối tượng trong việc thiết kế phần mềm, cung cấp một giao diện lớp có chức năng tạo ra một tập hợp các đối tượng liên quan hoặc phụ thuộc lẫn nhau mà không chỉ ra đó là những lớp cụ thể nào tại thời điểm thiết kế.[1] Mẫu thiết kế Abstract Factory đóng gói một nhóm những lớp đóng vai trò "sản xuất" (Factory) trong ứng dụng, đây là những lớp được dùng để tạo lập các đối tượng. Các lớp sản xuất này có chung một giao diện lập trình được kế thừa từ một lớp cha thuần ảo gọi là "lớp sản xuất ảo".

Có thể hiểu đơn giản Abstract Factory như 1 siêu nhà máy dùng để tạo ra các nhà máy (factory) khác.

Cấu trúc

  • AbstractFactory: định nghĩa một giao tiếp cho thao tác khởi tạo các "sản phẩm" ảo (AbstractProduct)
  • ConcreteFactory: thực thi giao tiếp AbstractFactory để tạo ra đối tượng cụ thể
  • AbstractProduct: định nghĩa một lớp ảo cho một loại đối tương "sản phẩm"
  • Product: kế thừa từ từ lớp "sản phẩm" ảo AbstractProduct, các lớp Product định nghĩa từ đối tượng cụ thể
  • Client: sử dụng các lớp AbstractFactory và AbstractProduct trong hệ thống

Ví dụ

abstractfactory_pattern_uml_diagram.jpg

Sơ đồ UML mô tả 1 dạng Abstract Factory

Như chúng ta thấy, khác với Factory Method, ở hình vẽ có thêm 1 lớp Producer, vai trò tương tự với lớp Factory ở phần 1. Có thể nói đây là 1 mô hình mở rộng của pattern Factory Method.

Bước 1

Tạo interface Shape (được hiểu là 1 AbstractProduct)

<?php
interface Shape {
    // Có thể định nghĩa sẵn const hoặc không :D
    const SQUARE = 1;
    const CIRCLE = 2;
    const RECTANGLE = 3;
    // general method
    function draw();
}

Bước 2

Tạo các class cụ thể implements cùng 1 interface Shape, đây chính là các Product đã được nhắc đến ở trên.

<?php
class Circle implements Shape
{
    public function draw() {
        // thực hiện code vẽ hình tròn tại đây
        echo "Draw circle";
    }
}
<?php
class Square implements Shape
{
    public function draw() {
        // thực hiện code vẽ hình vuông tại đây
        echo "Draw square";
    }
}
<?php
class Rectangle implements Shape
{
    public function draw() {
        // thực hiện code vẽ hình chữ nhật tại đây
        echo "Draw rectangle";
    }
}

Bước 3

Tạo interface Color (1 AbstractProduct tương tự Shape)

<?php
interface Color {
    // general method
    function fill();
}

Bước 4

Tương tự bước 2, tạo các class và implements cùng 1 interface Color, dĩ nhiên cũng là dạng sản phẩm - Product, của nhà máy - Factory

<?php
class Red implements Color
{
    public function fill() {
        echo "Filled red";
    }
}
<?php
class Green implements Color
{
    public function fill() {
        echo "Filled green";
    }
}
<?php
class Blue implements Color
{
    public function draw() {
        echo "Filled blue";
    }
}

Bước 5

Tạo ra 1 lớp Abstract với phương thức giáo tiếp đến thao tác khởi tạo các Shape và Color object - đây chính là khái niệm AbstractFactory được định nghĩa ở trên.

<?php

abstract class AbstractFactory {
   abstract function getColor($color);
   abstract function getShape($shape) ;
}

Bước 6

Tạo lớp Factory kế thừa AbstractFactory để thực hiện generate ra các object cụ thể dựa trên các thông tin được đưa ra. Các lớp này còn được gọi là ConcreteFactory.

Shape Factory

<?php
class ShapeFactory extends AbstractFactory
{
    public function getShape($type) {
        switch ($type) {
            case Shape::SQUARE:
                return new Square;
                break;
            case Shape::CIRCLE:
                return new Circle;
                break;
            case Shape::RECTANGLE:
                return new Rectangle;
                break;
            default:
                return nullbreak;
        }
        return null;
    }
}

Color Factory

<?php
class ColorFactory extends AbstractFactory
{
    public function getColor($color) {
        switch (strtolower($color)) {
            case 'red':
                return new Red();
                break;
            case 'blue':
                return new Blue()
                break;
            case 'green':
                return new Green()
                break;
            default:
                return null
                break;
        }
        return null;
    }
}

Bước 7

Tạo FactoryProducer để khởi tạo 1 abstract Shape/Color Factory

<?php

class FactoryProducer {
   public static function getFactory($choice)
   {
        $choice = strtolower($choice);
        if($choice == 'shape'){
            return new ShapeFactory;
        } elseif($choice == 'color')){
            return new ColorFactory;
        }
        return null;
   }
}

Bước 8

Như vậy là đã gần đầy đủ 1 mô hình AbstractFactory, chúng ta chỉ cần phần Client để sử dụng AbstractFactory và AbstractProduct trong hệ thống/apps của mình.

Tạo 1 file php bất kì để test thôi 😄

<?php
// dĩ nhiên là cần include các class cần sử dụng
include ...
// khởi tạo lớp ShapeFactory (tạo ra "nhà máy")
$shapeFactory = FactoryProducer::getFactory('shape');
// lấy object Shape với kiểu hình là Circle (lấy "sản phẩm")
$shape = $shapeFactory->getShape(Shape::CIRCLE);
//gọi method draw từ Shape Circle  (sử dụng)
$shape->draw();
//Tương tự với màu sắc
$colorFactory = FactoryProducer::getFactory('color');
$color= $shapeFactory->getColor('red');
$color->fill();

Bước 9

Cùng xem output

>Draw circle
>Filled red
```

# Kết

Ở bài viết này tôi đã giới thiệu khá cụ thể về 2 pattern thuộc Factory, tôi sẽ tìm hiểu tiếp về các pattern thông dụng nhất và sẽ chia sẻ lại trong các bài viết sau :D

Nguồn tham khảo:

- [**Wikipedia**](http://en.wikipedia.org/wiki/Software_design_pattern)
- [**Tutorials Point**](http://www.tutorialspoint.com/design_pattern)

Book:
- [**Head First Design Patterns**](http://it-ebooks.info/book/252/)
- [**Design Patterns: Elements of Reusable Object-Oriented Software**](http://www.uml.org.cn/c%2B%2B/pdf/DesignPatterns.pdf)

All Rights Reserved