本文主要討論下Web開發中,準確而言,是PHP開發中的相關的設計模式及其應用。有經驗的開發者肯定對於設計模式非常熟悉,但是本文主要是針對那 些初級的開發者。首先我們要搞清楚到底什麼是設計模式,設計模式並不是一種用來解釋的模式,它們並不是像鏈表那樣的常見的數據結構,也不是某種特殊的應用 或者框架設計。事實上,設計模式的解釋如下:
descriptions of communicating objects and classes that are customized to solve a general design problem in a particular context.
另一方面,設計模式提供了一種廣泛的可重用的方式來解決我們日常編程中常常遇見的問題。設計模式並不一定就是一個類庫或者第三方框架,它們更多的表 現爲一種思想並且廣泛地應用在系統中。它們也表現爲一種模式或者模板,可以在多個不同的場景下用於解決問題。設計模式可以用於加速開發,並且將很多大的想 法或者設計以一種簡單地方式實現。當然,雖然設計模式在開發中很有作用,但是千萬要避免在不適當的場景誤用它們。
目前常見的設計模式主要有23種,根據使用目標的不同可以分爲以下三大類:
-
創建模式:用於創建對象從而將某個對象從實現中解耦合。
-
架構模式:用於在不同的對象之間構造大的對象結構。
-
行爲模式:用於在不同的對象之間管理算法、關係以及職責。
Creational Patterns
Singleton(單例模式)
單例模式是最常見的模式之一,在Web應用的開發中,常常用於允許在運行時爲某個特定的類創建一個可訪問的實例。
在很多情況下,需要爲系統中的多個類創建單例的構造方式,這樣,可以建立一個通用的抽象父工廠方法:
Registry
註冊臺模式並不是很常見,它也不是一個典型的創建模式,只是爲了利用靜態方法更方便的存取數據。
<?php
/**
* Registry class
*/
class Package {
protected static $data = array();
public static function set($key, $value) {
self::$data[$key] = $value;
}
public static function get($key) {
return isset(self::$data[$key]) ? self::$data[$key] : null;
}
final public static function removeObject($key) {
if (array_key_exists($key, self::$data)) {
unset(self::$data[$key]);
}
}
}
Package::set('name', 'Package name');
print_r(Package::get('name'));
// Package name
Factory(工廠模式)
工廠模式是另一種非常常用的模式,正如其名字所示:確實是對象實例的生產工廠。某些意義上,工廠模式提供了通用的方法有助於我們去獲取對象,而不需要關心其具體的內在的實現。
<?php
interface Factory {
public function getProduct();
}
interface Product {
public function getName();
}
class FirstFactory implements Factory {
public function getProduct() {
return new FirstProduct();
}
}
class SecondFactory implements Factory {
public function getProduct() {
return new SecondProduct();
}
}
class FirstProduct implements Product {
public function getName() {
return 'The first product';
}
}
class SecondProduct implements Product {
public function getName() {
return 'Second product';
}
}
$factory = new FirstFactory();
$firstProduct = $factory->getProduct();
$factory = new SecondFactory();
$secondProduct = $factory->getProduct();
print_r($firstProduct->getName());
// The first product
print_r($secondProduct->getName());
// Second product
AbstractFactory(抽象工廠模式)
有些情況下我們需要根據不同的選擇邏輯提供不同的構造工廠,而對於多個工廠而言需要一個統一的抽象工廠:
<?php
class Config {
public static $factory = 1;
}
interface Product {
public function getName();
}
abstract class AbstractFactory {
public static function getFactory() {
switch (Config::$factory) {
case 1:
return new FirstFactory();
case 2:
return new SecondFactory();
}
throw new Exception('Bad config');
}
abstract public function getProduct();
}
class FirstFactory extends AbstractFactory {
public function getProduct() {
return new FirstProduct();
}
}
class FirstProduct implements Product {
public function getName() {
return 'The product from the first factory';
}
}
class SecondFactory extends AbstractFactory {
public function getProduct() {
return new SecondProduct();
}
}
class SecondProduct implements Product {
public function getName() {
return 'The product from second factory';
}
}
$firstProduct = AbstractFactory::getFactory()->getProduct();
Config::$factory = 2;
$secondProduct = AbstractFactory::getFactory()->getProduct();
print_r($firstProduct->getName());
// The first product from the first factory
print_r($secondProduct->getName());
// Second product from second factory
Object pool(對象池)
對象池可以用於構造並且存放一系列的對象並在需要時獲取調用:
<?php
class Product {
protected $id;
public function __construct($id) {
$this->id = $id;
}
public function getId() {
return $this->id;
}
}
class Factory {
protected static $products = array();
public static function pushProduct(Product $product) {
self::$products[$product->getId()] = $product;
}
public static function getProduct($id) {
return isset(self::$products[$id]) ? self::$products[$id] : null;
}
public static function removeProduct($id) {
if (array_key_exists($id, self::$products)) {
unset(self::$products[$id]);
}
}
}
Factory::pushProduct(new Product('first'));
Factory::pushProduct(new Product('second'));
print_r(Factory::getProduct('first')->getId());
// first
print_r(Factory::getProduct('second')->getId());
// second
Lazy Initialization(延遲初始化)
對於某個變量的延遲初始化也是常常被用到的,對於一個類而言往往並不知道它的哪個功能會被用到,而部分功能往往是僅僅被需要使用一次。
<?php
interface Product {
public function getName();
}
class Factory {
protected $firstProduct;
protected $secondProduct;
public function getFirstProduct() {
if (!$this->firstProduct) {
$this->firstProduct = new FirstProduct();
}
return $this->firstProduct;
}
public function getSecondProduct() {
if (!$this->secondProduct) {
$this->secondProduct = new SecondProduct();
}
return $this->secondProduct;
}
}
class FirstProduct implements Product {
public function getName() {
return 'The first product';
}
}
class SecondProduct implements Product {
public function getName() {
return 'Second product';
}
}
$factory = new Factory();
print_r($factory->getFirstProduct()->getName());
// The first product
print_r($factory->getSecondProduct()->getName());
// Second product
print_r($factory->getFirstProduct()->getName());
// The first product
Prototype(原型模式)
有些時候,部分對象需要被初始化多次。而特別是在如果初始化需要耗費大量時間與資源的時候進行預初始化並且存儲下這些對象。
<?php
interface Product {
}
class Factory {
private $product;
public function __construct(Product $product) {
$this->product = $product;
}
public function getProduct() {
return clone $this->product;
}
}
class SomeProduct implements Product {
public $name;
}
$prototypeFactory = new Factory(new SomeProduct());
$firstProduct = $prototypeFactory->getProduct();
$firstProduct->name = 'The first product';
$secondProduct = $prototypeFactory->getProduct();
$secondProduct->name = 'Second product';
print_r($firstProduct->name);
// The first product
print_r($secondProduct->name);
// Second product
Builder(構造者)
構造者模式主要在於創建一些複雜的對象:
<?php
class Product {
private $name;
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Builder {
protected $product;
final public function getProduct() {
return $this->product;
}
public function buildProduct() {
$this->product = new Product();
}
}
class FirstBuilder extends Builder {
public function buildProduct() {
parent::buildProduct();
$this->product->setName('The product of the first builder');
}
}
class SecondBuilder extends Builder {
public function buildProduct() {
parent::buildProduct();
$this->product->setName('The product of second builder');
}
}
class Factory {
private $builder;
public function __construct(Builder $builder) {
$this->builder = $builder;
$this->builder->buildProduct();
}
public function getProduct() {
return $this->builder->getProduct();
}
}
$firstDirector = new Factory(new FirstBuilder());
$secondDirector = new Factory(new SecondBuilder());
print_r($firstDirector->getProduct()->getName());
// The product of the first builder
print_r($secondDirector->getProduct()->getName());
// The product of second builder
Structural Patterns
Decorator(裝飾器模式)
裝飾器模式允許我們根據運行時不同的情景動態地爲某個對象調用前後添加不同的行爲動作。
<?php
class HtmlTemplate {
// any parent class methods
}
class Template1 extends HtmlTemplate {
protected $_html;
public function __construct() {
$this->_html = "<p>__text__</p>";
}
public function set($html) {
$this->_html = $html;
}
public function render() {
echo $this->_html;
}
}
class Template2 extends HtmlTemplate {
protected $_element;
public function __construct($s) {
$this->_element = $s;
$this->set("<h2>" . $this->_html . "</h2>");
}
public function __call($name, $args) {
$this->_element->$name($args[0]);
}
}
class Template3 extends HtmlTemplate {
protected $_element;
public function __construct($s) {
$this->_element = $s;
$this->set("<u>" . $this->_html . "</u>");
}
public function __call($name, $args) {
$this->_element->$name($args[0]);
}
}
Adapter(適配器模式)
這種模式允許使用不同的接口重構某個類,可以允許使用不同的調用方式進行調用:
<?php
class SimpleBook {
private $author;
private $title;
function __construct($author_in, $title_in) {
$this->author = $author_in;
$this->title = $title_in;
}
function getAuthor() {
return $this->author;
}
function getTitle() {
return $this->title;
}
}
class BookAdapter {
private $book;
function __construct(SimpleBook $book_in) {
$this->book = $book_in;
}
function getAuthorAndTitle() {
return $this->book->getTitle().' by '.$this->book->getAuthor();
}
}
// Usage
$book = new SimpleBook("Gamma, Helm, Johnson, and Vlissides", "Design Patterns");
$bookAdapter = new BookAdapter($book);
echo 'Author and Title: '.$bookAdapter->getAuthorAndTitle();
function echo $line_in) {
echo $line_in."<br/>";
}
Behavioral Patterns
Strategy(策略模式)
測試模式主要爲了讓客戶類能夠更好地使用某些算法而不需要知道其具體的實現。
<?php
interface OutputInterface {
public function load();
}
class SerializedArrayOutput implements OutputInterface {
public function load() {
return serialize($arrayOfData);
}
}
class JsonStringOutput implements OutputInterface {
public function load() {
return json_encode($arrayOfData);
}
}
class ArrayOutput implements OutputInterface {
public function load() {
return $arrayOfData;
}
}
Observer(觀察者模式)
某個對象可以被設置爲是可觀察的,只要通過某種方式允許其他對象註冊爲觀察者。每當被觀察的對象改變時,會發送信息給觀察者。
<?php
interface Observer {
function onChanged($sender, $args);
}
interface Observable {
function addObserver($observer);
}
class CustomerList implements Observable {
private $_observers = array();
public function addCustomer($name) {
foreach($this->_observers as $obs)
$obs->onChanged($this, $name);
}
public function addObserver($observer) {
$this->_observers []= $observer;
}
}
class CustomerListLogger implements Observer {
public function onChanged($sender, $args) {
echo( "'$args' Customer has been added to the list \n" );
}
}
$ul = new UserList();
$ul->addObserver( new CustomerListLogger() );
$ul->addCustomer( "Jack" );
Chain of responsibility(責任鏈模式)
這種模式有另一種稱呼:控制鏈模式。它主要由一系列對於某些命令的處理器構成,每個查詢會在處理器構成的責任鏈中傳遞,在每個交匯點由處理器判斷是否需要對它們進行響應與處理。每次的處理程序會在有處理器處理這些請求時暫停。
<?php
interface Command {
function onCommand($name, $args);
}
class CommandChain {
private $_commands = array();
public function addCommand($cmd) {
$this->_commands[]= $cmd;
}
public function runCommand($name, $args) {
foreach($this->_commands as $cmd) {
if ($cmd->onCommand($name, $args))
return;
}
}
}
class CustCommand implements Command {
public function onCommand($name, $args) {
if ($name != 'addCustomer')
return false;
echo("This is CustomerCommand handling 'addCustomer'\n");
return true;
}
}
class MailCommand implements Command {
public function onCommand($name, $args) {
if ($name != 'mail')
return false;
echo("This is MailCommand handling 'mail'\n");
return true;
}
}
$cc = new CommandChain();
$cc->addCommand( new CustCommand());
$cc->addCommand( new MailCommand());
$cc->runCommand('addCustomer', null);
$cc->runCommand('mail', null);