本文主要讨论下Web开发中,准确而言,是PHP开发中的相关的设计模式及其应用。有经验的开发者肯定对于设计模式非常熟悉,但是本文主要是针对那些初级的开发者。首先我们要搞清楚到底什么是设计模式,设计模式并不是一种用来解释的模式,它们并不是像链表那样的常见的数据结构,也不是某种特殊的应用或者框架设计。事实上,设计模式的解释如下:

descriptions of communicating objects and classes that are customized to solve a general design problem in a particular context.

  另一方面,设计模式提供了一种广泛的可重用的方式来解决我们日常编程中常常遇见的问题。设计模式并不一定就是一个类库或者第三方框架,它们更多的表现为一种思想并且广泛地应用在系统中。它们也表现为一种模式或者模板,可以在多个不同的场景下用于解决问题。设计模式可以用于加速开发,并且将很多大的想法或者设计以一种简单地方式实现。当然,虽然设计模式在开发中很有作用,但是千万要避免在不适当的场景误用它们。

  目前常见的设计模式主要有23种,根据使用目标的不同可以分为以下三大类:

  1. 创建模式:用于创建对象从而将某个对象从实现中解耦合。
  2. 架构模式:用于在不同的对象之间构造大的对象结构。
  3. 行为模式:用于在不同的对象之间管理算法、关系以及职责。

Creational Patterns

  • Singleton(单例模式)

  单例模式是最常见的模式之一,在Web应用的开发中,常常用于允许在运行时为某个特定的类创建一个可访问的实例。

  1. <?php
  2. /**
  3. * Singleton class
  4. */
  5. final class Product
  6. {
  7. /**
  8. * @var self
  9. */
  10. private static $instance;
  11. /**
  12. * @var mixed
  13. */
  14. public $mix;
  15. /**
  16. * Return self instance
  17. *
  18. * @return self
  19. */
  20. public static function getInstance() {
  21. if (!(self::$instance instanceof self)) {
  22. self::$instance = new self();
  23. }
  24. return self::$instance;
  25. }
  26. private function __construct() {
  27. }
  28. private function __clone() {
  29. }
  30. }
  31. $firstProduct = Product::getInstance();
  32. $secondProduct = Product::getInstance();
  33. $firstProduct->mix = 'test';
  34. $secondProduct->mix = 'example';
  35. print_r($firstProduct->mix);
  36. // example
  37. print_r($secondProduct->mix);
  38. // example

  在很多情况下,需要为系统中的多个类创建单例的构造方式,这样,可以建立一个通用的抽象父工厂方法:

  1. <?php
  2. abstract class FactoryAbstract {
  3. protected static $instances = array();
  4. public static function getInstance() {
  5. $className = static::getClassName();
  6. if (!(self::$instances[$className] instanceof $className)) {
  7. self::$instances[$className] = new $className();
  8. }
  9. return self::$instances[$className];
  10. }
  11. public static function removeInstance() {
  12. $className = static::getClassName();
  13. if (array_key_exists($className, self::$instances)) {
  14. unset(self::$instances[$className]);
  15. }
  16. }
  17. final protected static function getClassName() {
  18. return get_called_class();
  19. }
  20. protected function __construct() { }
  21. final protected function __clone() { }
  22. }
  23. abstract class Factory extends FactoryAbstract {
  24. final public static function getInstance() {
  25. return parent::getInstance();
  26. }
  27. final public static function removeInstance() {
  28. parent::removeInstance();
  29. }
  30. }
  31. // using:
  32. class FirstProduct extends Factory {
  33. public $a = [];
  34. }
  35. class SecondProduct extends FirstProduct {
  36. }
  37. FirstProduct::getInstance()->a[] = 1;
  38. SecondProduct::getInstance()->a[] = 2;
  39. FirstProduct::getInstance()->a[] = 3;
  40. SecondProduct::getInstance()->a[] = 4;
  41. print_r(FirstProduct::getInstance()->a);
  42. // array(1, 3)
  43. print_r(SecondProduct::getInstance()->a);
  44. // array(2, 4)
  • Registry

  注册台模式并不是很常见,它也不是一个典型的创建模式,只是为了利用静态方法更方便的存取数据。

  1. <?php
  2. /**
  3. * Registry class
  4. */
  5. class Package {
  6. protected static $data = array();
  7. public static function set($key, $value) {
  8. self::$data[$key] = $value;
  9. }
  10. public static function get($key) {
  11. return isset(self::$data[$key]) ? self::$data[$key] : null;
  12. }
  13. final public static function removeObject($key) {
  14. if (array_key_exists($key, self::$data)) {
  15. unset(self::$data[$key]);
  16. }
  17. }
  18. }
  19. Package::set('name', 'Package name');
  20. print_r(Package::get('name'));
  • Factory(工厂模式)

  工厂模式是另一种非常常用的模式,正如其名字所示:确实是对象实例的生产工厂。某些意义上,工厂模式提供了通用的方法有助于我们去获取对象,而不需要关心其具体的内在的实现。

  1. <?php
  2. interface Factory {
  3. public function getProduct();
  4. }
  5. interface Product {
  6. public function getName();
  7. }
  8. class FirstFactory implements Factory {
  9. public function getProduct() {
  10. return new FirstProduct();
  11. }
  12. }
  13. class SecondFactory implements Factory {
  14. public function getProduct() {
  15. return new SecondProduct();
  16. }
  17. }
  18. class FirstProduct implements Product {
  19. public function getName() {
  20. return 'The first product';
  21. }
  22. }
  23. class SecondProduct implements Product {
  24. public function getName() {
  25. return 'Second product';
  26. }
  27. }
  28. $factory = new FirstFactory();
  29. $firstProduct = $factory->getProduct();
  30. $factory = new SecondFactory();
  31. $secondProduct = $factory->getProduct();
  32. print_r($firstProduct->getName());
  33. // The first product
  34. print_r($secondProduct->getName());
  35. // Second product
  • AbstractFactory(抽象工厂模式)

  有些情况下我们需要根据不同的选择逻辑提供不同的构造工厂,而对于多个工厂而言需要一个统一的抽象工厂:

  1. <?php
  2. class Config {
  3. public static $factory = 1;
  4. }
  5. interface Product {
  6. public function getName();
  7. }
  8. abstract class AbstractFactory {
  9. public static function getFactory() {
  10. switch (Config::$factory) {
  11. case 1:
  12. return new FirstFactory();
  13. case 2:
  14. return new SecondFactory();
  15. }
  16. throw new Exception('Bad config');
  17. }
  18. abstract public function getProduct();
  19. }
  20. class FirstFactory extends AbstractFactory {
  21. public function getProduct() {
  22. return new FirstProduct();
  23. }
  24. }
  25. class FirstProduct implements Product {
  26. public function getName() {
  27. return 'The product from the first factory';
  28. }
  29. }
  30. class SecondFactory extends AbstractFactory {
  31. public function getProduct() {
  32. return new SecondProduct();
  33. }
  34. }
  35. class SecondProduct implements Product {
  36. public function getName() {
  37. return 'The product from second factory';
  38. }
  39. }
  40. $firstProduct = AbstractFactory::getFactory()->getProduct();
  41. Config::$factory = 2;
  42. $secondProduct = AbstractFactory::getFactory()->getProduct();
  43. print_r($firstProduct->getName());
  44. // The first product from the first factory
  45. print_r($secondProduct->getName());
  46. // Second product from second factory
  • Object pool(对象池)

  对象池可以用于构造并且存放一系列的对象并在需要时获取调用:

  1. <?php
  2. class Product {
  3. protected $id;
  4. public function __construct($id) {
  5. $this->id = $id;
  6. }
  7. public function getId() {
  8. return $this->id;
  9. }
  10. }
  11. class Factory {
  12. protected static $products = array();
  13. public static function pushProduct(Product $product) {
  14. self::$products[$product->getId()] = $product;
  15. }
  16. public static function getProduct($id) {
  17. return isset(self::$products[$id]) ? self::$products[$id] : null;
  18. }
  19. public static function removeProduct($id) {
  20. if (array_key_exists($id, self::$products)) {
  21. unset(self::$products[$id]);
  22. }
  23. }
  24. }
  25. Factory::pushProduct(new Product('first'));
  26. Factory::pushProduct(new Product('second'));
  27. print_r(Factory::getProduct('first')->getId());
  28. // first
  29. print_r(Factory::getProduct('second')->getId());
  30. // second
  • Lazy Initialization(延迟初始化)

  对于某个变量的延迟初始化也是常常被用到的,对于一个类而言往往并不知道它的哪个功能会被用到,而部分功能往往是仅仅被需要使用一次。

  1. <?php
  2. interface Product {
  3. public function getName();
  4. }
  5. class Factory {
  6. protected $firstProduct;
  7. protected $secondProduct;
  8. public function getFirstProduct() {
  9. if (!$this->firstProduct) {
  10. $this->firstProduct = new FirstProduct();
  11. }
  12. return $this->firstProduct;
  13. }
  14. public function getSecondProduct() {
  15. if (!$this->secondProduct) {
  16. $this->secondProduct = new SecondProduct();
  17. }
  18. return $this->secondProduct;
  19. }
  20. }
  21. class FirstProduct implements Product {
  22. public function getName() {
  23. return 'The first product';
  24. }
  25. }
  26. class SecondProduct implements Product {
  27. public function getName() {
  28. return 'Second product';
  29. }
  30. }
  31. $factory = new Factory();
  32. print_r($factory->getFirstProduct()->getName());
  33. // The first product
  34. print_r($factory->getSecondProduct()->getName());
  35. // Second product
  36. print_r($factory->getFirstProduct()->getName());
  37. // The first product
  • Prototype(原型模式)

  有些时候,部分对象需要被初始化多次。而特别是在如果初始化需要耗费大量时间与资源的时候进行预初始化并且存储下这些对象。

  1. <?php
  2. interface Product {
  3. }
  4. class Factory {
  5. private $product;
  6. public function __construct(Product $product) {
  7. $this->product = $product;
  8. }
  9. public function getProduct() {
  10. return clone $this->product;
  11. }
  12. }
  13. class SomeProduct implements Product {
  14. public $name;
  15. }
  16. $prototypeFactory = new Factory(new SomeProduct());
  17. $firstProduct = $prototypeFactory->getProduct();
  18. $firstProduct->name = 'The first product';
  19. $secondProduct = $prototypeFactory->getProduct();
  20. $secondProduct->name = 'Second product';
  21. print_r($firstProduct->name);
  22. // The first product
  23. print_r($secondProduct->name);
  24. // Second product
  • Builder(构造者)

  构造者模式主要在于创建一些复杂的对象:

  1. <?php
  2. class Product {
  3. private $name;
  4. public function setName($name) {
  5. $this->name = $name;
  6. }
  7. public function getName() {
  8. return $this->name;
  9. }
  10. }
  11. abstract class Builder {
  12. protected $product;
  13. final public function getProduct() {
  14. return $this->product;
  15. }
  16. public function buildProduct() {
  17. $this->product = new Product();
  18. }
  19. }
  20. class FirstBuilder extends Builder {
  21. public function buildProduct() {
  22. parent::buildProduct();
  23. $this->product->setName('The product of the first builder');
  24. }
  25. }
  26. class SecondBuilder extends Builder {
  27. public function buildProduct() {
  28. parent::buildProduct();
  29. $this->product->setName('The product of second builder');
  30. }
  31. }
  32. class Factory {
  33. private $builder;
  34. public function __construct(Builder $builder) {
  35. $this->builder = $builder;
  36. $this->builder->buildProduct();
  37. }
  38. public function getProduct() {
  39. return $this->builder->getProduct();
  40. }
  41. }
  42. $firstDirector = new Factory(new FirstBuilder());
  43. $secondDirector = new Factory(new SecondBuilder());
  44. print_r($firstDirector->getProduct()->getName());
  45. // The product of the first builder
  46. print_r($secondDirector->getProduct()->getName());
  47. // The product of second builder

Structural Patterns

  • Decorator(装饰器模式)

  装饰器模式允许我们根据运行时不同的情景动态地为某个对象调用前后添加不同的行为动作。

  1. <?php
  2. class HtmlTemplate {
  3. // any parent class methods
  4. }
  5. class Template1 extends HtmlTemplate {
  6. protected $_html;
  7. public function __construct() {
  8. $this->_html = "<p>__text__</p>";
  9. }
  10. public function set($html) {
  11. $this->_html = $html;
  12. }
  13. public function render() {
  14. echo $this->_html;
  15. }
  16. }
  17. class Template2 extends HtmlTemplate {
  18. protected $_element;
  19. public function __construct($s) {
  20. $this->_element = $s;
  21. $this->set("<h2>" . $this->_html . "</h2>");
  22. }
  23. public function __call($name, $args) {
  24. $this->_element->$name($args[0]);
  25. }
  26. }
  27. class Template3 extends HtmlTemplate {
  28. protected $_element;
  29. public function __construct($s) {
  30. $this->_element = $s;
  31. $this->set("<u>" . $this->_html . "</u>");
  32. }
  33. public function __call($name, $args) {
  34. $this->_element->$name($args[0]);
  35. }
  36. }
  • Adapter(适配器模式)

  这种模式允许使用不同的接口重构某个类,可以允许使用不同的调用方式进行调用:

  1. <?php
  2. class SimpleBook {
  3. private $author;
  4. private $title;
  5. function __construct($author_in, $title_in) {
  6. $this->author = $author_in;
  7. $this->title = $title_in;
  8. }
  9. function getAuthor() {
  10. return $this->author;
  11. }
  12. function getTitle() {
  13. return $this->title;
  14. }
  15. }
  16. class BookAdapter {
  17. private $book;
  18. function __construct(SimpleBook $book_in) {
  19. $this->book = $book_in;
  20. }
  21. function getAuthorAndTitle() {
  22. return $this->book->getTitle().' by '.$this->book->getAuthor();
  23. }
  24. }
  25. // Usage
  26. $book = new SimpleBook("Gamma, Helm, Johnson, and Vlissides", "Design Patterns");
  27. $bookAdapter = new BookAdapter($book);
  28. echo 'Author and Title: '.$bookAdapter->getAuthorAndTitle();
  29. function echo $line_in) {
  30. echo $line_in."<br/>";
  31. }

Behavioral Patterns

  • Strategy(策略模式)

  测试模式主要为了让客户类能够更好地使用某些算法而不需要知道其具体的实现。

  1. <?php
  2. interface OutputInterface {
  3. public function load();
  4. }
  5. class SerializedArrayOutput implements OutputInterface {
  6. public function load() {
  7. return serialize($arrayOfData);
  8. }
  9. }
  10. class JsonStringOutput implements OutputInterface {
  11. public function load() {
  12. return json_encode($arrayOfData);
  13. }
  14. }
  15. class ArrayOutput implements OutputInterface {
  16. public function load() {
  17. return $arrayOfData;
  18. }
  19. }
  • Observer(观察者模式)

  某个对象可以被设置为是可观察的,只要通过某种方式允许其他对象注册为观察者。每当被观察的对象改变时,会发送信息给观察者。

  1. <?php
  2. interface Observer {
  3. function onChanged($sender, $args);
  4. }
  5. interface Observable {
  6. function addObserver($observer);
  7. }
  8. class CustomerList implements Observable {
  9. private $_observers = array();
  10. public function addCustomer($name) {
  11. foreach($this->_observers as $obs)
  12. $obs->onChanged($this, $name);
  13. }
  14. public function addObserver($observer) {
  15. $this->_observers []= $observer;
  16. }
  17. }
  18. class CustomerListLogger implements Observer {
  19. public function onChanged($sender, $args) {
  20. echo( "'$args' Customer has been added to the list \n" );
  21. }
  22. }
  23. $ul = new UserList();
  24. $ul->addObserver( new CustomerListLogger() );
  25. $ul->addCustomer( "Jack" );
  • Chain of responsibility(责任链模式)

  这种模式有另一种称呼:控制链模式。它主要由一系列对于某些命令的处理器构成,每个查询会在处理器构成的责任链中传递,在每个交汇点由处理器判断是否需要对它们进行响应与处理。每次的处理程序会在有处理器处理这些请求时暂停。

  1. <?php
  2. interface Command {
  3. function onCommand($name, $args);
  4. }
  5. class CommandChain {
  6. private $_commands = array();
  7. public function addCommand($cmd) {
  8. $this->_commands[]= $cmd;
  9. }
  10. public function runCommand($name, $args) {
  11. foreach($this->_commands as $cmd) {
  12. if ($cmd->onCommand($name, $args))
  13. return;
  14. }
  15. }
  16. }
  17. class CustCommand implements Command {
  18. public function onCommand($name, $args) {
  19. if ($name != 'addCustomer')
  20. return false;
  21. echo("This is CustomerCommand handling 'addCustomer'\n");
  22. return true;
  23. }
  24. }
  25. class MailCommand implements Command {
  26. public function onCommand($name, $args) {
  27. if ($name != 'mail')
  28. return false;
  29. echo("This is MailCommand handling 'mail'\n");
  30. return true;
  31. }
  32. }
  33. $cc = new CommandChain();
  34. $cc->addCommand( new CustCommand());
  35. $cc->addCommand( new MailCommand());
  36. $cc->runCommand('addCustomer', null);
  37. $cc->runCommand('mail', null);