GoF 设计模式

有很多事情可以使一个优秀的软件开发人员成为可能。设计模式的知识和使用就是其中之一。设计模式使开发人员能够使用各种软件交互的知名名称进行交流。无论某人是PHP、Python、C#、Ruby或任何其他语言的开发者,设计模式都能为经常出现的软件问题提供语言不可知的解决方案。

设计模式的概念出现在1994年,作为《可重用的面向对象的软件要素》一书的一部分。该书详细介绍了23种不同的设计模式,由四位作者Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides撰写。作者们通常被称为四人帮(GoF),所介绍的设计模式有时也被称为GoF设计模式。在二十多年后的今天,如果不将设计模式作为实现的一部分,设计出可扩展、可重用、可维护和可适应的软件几乎是不可能的。

有三种类型的设计模式,我们将在本章介绍。

  • 创建型
  • 结构型
  • 行为型

创建型

创建型,顾名思义就是为我们创建对象,所以我们不必直接实例化它们。实现创建型使我们的应用程序具有一定的灵活性,应用程序本身可以决定在给定时间内实例化哪些对象。以下是我们归类为创建型的模式列表。

  • 抽象工厂模式
  • 生成器模式
  • 工厂方法模式
  • 原型模式
  • 单例模式

{% hint style=”info” %} 关于创建模式的更多信息,请参见https://en.wikipedia.org/wiki/Creational\_pattern。 {% endhint %}

抽象工厂模式

构建可移植的应用程序需要高水平的依赖性封装。抽象工厂通过抽象化相关或依赖对象家族的创建来实现这一点。客户端从来没有直接创建这些平台对象,工厂为他们做了这些工作,使得在不改变使用它们的代码的情况下,甚至在运行时也可以互换具体实现。

下面是一个抽象工厂模式实现的例子。

  1. interface Button {
  2. public function render();
  3. }
  4. interface GUIFactory {
  5. public function createButton();
  6. }
  7. class SubmitButton implements Button {
  8. public function render() {
  9. echo 'Render Submit Button';
  10. }
  11. }
  12. class ResetButton implements Button {
  13. public function render() {
  14. echo 'Render Reset Button';
  15. }
  16. }
  17. class SubmitFactory implements GUIFactory {
  18. public function createButton() {
  19. return new SubmitButton();
  20. }
  21. }
  22. class ResetFactory implements GUIFactory {
  23. public function createButton() {
  24. return new ResetButton();
  25. }
  26. }
  27. // Client
  28. $submitFactory = new SubmitFactory();
  29. $button = $submitFactory->createButton();
  30. $button->render();
  31. $resetFactory = new ResetFactory();
  32. $button = $resetFactory->createButton();
  33. $button->render();

我们首先创建一个接口Button,之后由我们的SubmitButtonResetButton具体类实现。GUIFactoryResetFactory实现了GUIFactory接口,它指定了createButton方法。然后客户端只需实例化工厂并调用createButton,就会返回一个合适的按钮实例,我们调用渲染方法。

生成器模式

生成器模式将复杂对象的构建与表示分离,使得同一个构建过程可以创建不同的表示。有些生成器模式是在一次调用中构建一个产品,而生成器模式则是在 director 的控制下逐步完成。

下面是一个生成器模式的实现实例。

  1. class Car {
  2. public function getWheels() {
  3. /* implementation... */
  4. }
  5. public function setWheels($wheels) {
  6. /* implementation... */
  7. }
  8. public function getColour($colour) {
  9. /* implementation... */
  10. }
  11. public function setColour() {
  12. /* implementation... */
  13. }
  14. }
  15. interface CarBuilderInterface {
  16. public function setColour($colour);
  17. public function setWheels($wheels);
  18. public function getResult();
  19. }
  20. class CarBuilder implements CarBuilderInterface {
  21. private $car;
  22. public function __construct() {
  23. $this->car = new Car();
  24. }
  25. public function setColour($colour) {
  26. $this->car->setColour($colour);
  27. return $this;
  28. }
  29. public function setWheels($wheels) {
  30. $this->car->setWheels($wheels);
  31. return $this;
  32. }
  33. public function getResult() {
  34. return $this->car;
  35. }
  36. }
  37. class CarBuildDirector {
  38. private $builder;
  39. public function __construct(CarBuilder $builder) {
  40. $this->builder = $builder;
  41. }
  42. public function build() {
  43. $this->builder->setColour('Red');
  44. $this->builder->setWheels(4);
  45. return $this;
  46. }
  47. public function getCar() {
  48. return $this->builder->getResult();
  49. }
  50. }
  51. // Client
  52. $carBuilder = new CarBuilder();
  53. $carBuildDirector = new CarBuildDirector($carBuilder);
  54. $car = $carBuildDirector->build()->getCar();

我们首先创建了一个具体的Car类,其中有几个方法定义了汽车的一些基本特性。然后我们创建了一个CarBuilderInterface,它将控制其中的一些特性,并得到最终的结果(汽车)。然后,具体的CarBuilder类实现了CarBuilderInterface,接着是具体的CarBuildDirector类,它定义了buildgetCar方法。然后,客户端简单地实例化一个新的CarBuilder实例,将其作为构造参数传递给一个新的CarBuildDirector实例。最后,我们调用CarBuildDirectorbuild和getCar方法来获取实际的汽车Car实例。

工厂方法模式

工厂方法模式处理了创建对象的问题,而不必指定将要创建的对象的确切类。

下面是一个工厂方法模式的实现例子。

  1. interface Product {
  2. public function getType();
  3. }
  4. interface ProductFactory {
  5. public function makeProduct();
  6. }
  7. class SimpleProduct implements Product {
  8. public function getType() {
  9. return 'SimpleProduct';
  10. }
  11. }
  12. class SimpleProductFactory implements ProductFactory {
  13. public function makeProduct() {
  14. return new SimpleProduct();
  15. }
  16. }
  17. /* Client */
  18. $factory = new SimpleProductFactory();
  19. $product = $factory->makeProduct();
  20. echo $product->getType(); //outputs: SimpleProduct

我们首先创建一个ProductFactoryProduct接口。SimpleProductFactory实现了ProductFactory,并通过其makeProduct方法返回新的产品实例。SimpleProduct类实现Product,并返回产品类型。最后,客户端创建SimpleProductFactory的实例,对其调用makeProduct方法。makeProduct返回Product的实例,其getType方法返回SimpleProduct字符串。

原型模式

原型模式通过使用克隆来复制其他对象。这意味着我们不是使用new关键字来实例化新的对象。PHP提供了一个clone关键字,它可以对一个对象进行浅层复制,从而提供了非常直接的原型模式实现。浅层复制不复制引用,只复制新对象的值。我们可以进一步在我们的类上利用神奇的__clone方法,以实现更强大的克隆行为。

下面是一个原型模式实现的例子。

  1. class User {
  2. public $name;
  3. public $email;
  4. }
  5. class Employee extends User {
  6. public function __construct() {
  7. $this->name = 'Johhn Doe';
  8. $this->email = 'john.doe@fake.mail';
  9. }
  10. public function info() {
  11. return sprintf('%s, %s', $this->name, $this->email);
  12. }
  13. public function __clone() {
  14. /* additional changes for (after)clone behavior? */
  15. }
  16. }
  17. $employee = new Employee();
  18. echo $employee->info();
  19. $director = clone $employee;
  20. $director->name = 'Jane Doe';
  21. $director->email = 'jane.doe@fake.mail';
  22. echo $director->info(); //outputs: Jane Doe, jane.doe@fake.mail

我们先创建一个简单的User类。然后,Employee扩展了User,同时在其构造函数中设置了名称和电子邮件。然后客户端通过new关键字实例化Employee,并将其克隆到director 变量中。现在$director变量是一个新的实例,这个实例不是通过new关键字制作的,而是通过克隆,使用clone关键字制作的。改变$director上的名字和邮箱,不会影响$employee

单例模式

单例模式的目的是将类的实例化限制在一个对象上。它是通过在类中创建一个方法来实现的,如果一个类的实例不存在,该方法就会创建一个新的实例。如果一个对象实例已经存在,该方法只是返回一个现有对象的引用。

下面是一个单例模式实现的例子。

  1. class Logger {
  2. private static $instance;
  3. public static function getInstance() {
  4. if (!isset(self::$instance)) {
  5. self::$instance = new self;
  6. }
  7. return self::$instance;
  8. }
  9. public function logNotice($msg) {
  10. return 'logNotice: ' . $msg;
  11. }
  12. public function logWarning($msg) {
  13. return 'logWarning: ' . $msg;
  14. }
  15. public function logError($msg) {
  16. return 'logError: ' . $msg;
  17. }
  18. }
  19. // Client
  20. echo Logger::getInstance()->logNotice('test-notice');
  21. echo Logger::getInstance()->logWarning('test-warning');
  22. echo Logger::getInstance()->logError('test-error');
  23. // Outputs:
  24. // logNotice: test-notice
  25. // logWarning: test-warning
  26. // logError: test-error

我们首先创建了一个具有静态$instance成员的Logger类,以及总是返回该类的单个实例的getInstance方法。然后我们添加了一些示例方法来演示客户端在单个实例上执行各种方法。