行为型

行为型解决的是不同对象之间的通信挑战。它们描述了不同的对象和类之间如何相互发送消息以使事情发生。以下是我们归类为行为型的模式列表。

  • 责任链模式
  • 命令模式
  • 解析器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 模板方法模式
  • 访问者模式

责任链模式

责任链模式通过使一个以上的对象以链式方式处理请求,将请求的发送方与接收方解耦。各种类型的处理对象可以动态地添加到链中。使用递归组成链,可以实现无限数量的处理对象。

下面是一个责任链模式的实现例子。

  1. abstract class SocialNotifier {
  2. private $notifyNext = null;
  3. public function notifyNext(SocialNotifier $notifier) {
  4. $this->notifyNext = $notifier;
  5. return $this->notifyNext;
  6. }
  7. final public function push($message) {
  8. $this->publish($message);
  9. if ($this->notifyNext !== null) {
  10. $this->notifyNext->push($message);
  11. }
  12. }
  13. abstract protected function publish($message);
  14. }
  15. class TwitterSocialNotifier extends SocialNotifier {
  16. public function publish($message) {
  17. // Implementation...
  18. }
  19. }
  20. class FacebookSocialNotifier extends SocialNotifier {
  21. protected function publish($message) {
  22. // Implementation...
  23. }
  24. }
  25. class PinterestSocialNotifier extends SocialNotifier {
  26. protected function publish($message) {
  27. // Implementation...
  28. }
  29. }
  30. // Client
  31. $notifier = new TwitterSocialNotifier();
  32. $notifier->notifyNext(new FacebookSocialNotifier())
  33. ->notifyNext(new PinterestSocialNotifier());
  34. $notifier->push('Awesome new product available!');

我们首先创建了一个抽象的SocialNotifier类,并实现了抽象方法publishnotifyNextpush方法。然后我们定义了TwitterSocialNotifierFacebookSocialNotifierPinterestSocialNotifier,它们都是对抽象SocialNotifier的扩展。客户端首先实例化TwitterSocialNotifier,然后是两次notifyNext调用,在调用最后的push方法之前,向它传递另外两个notifier类型的实例。

命令模式

命令模式将执行某些操作的对象与知道如何使用它的对象解耦。它通过封装以后执行某个操作所需的所有相关信息来实现。这意味着对象、方法名和方法参数的信息。

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

  1. interface LightBulbCommand {
  2. public function execute();
  3. }
  4. class LightBulbControl {
  5. public function turnOn() {
  6. echo 'LightBulb turnOn';
  7. }
  8. public function turnOff() {
  9. echo 'LightBulb turnOff';
  10. }
  11. }
  12. class TurnOnLightBulb implements LightBulbCommand {
  13. private $lightBulbControl;
  14. public function __construct(LightBulbControl $lightBulbControl) {
  15. $this->lightBulbControl = $lightBulbControl;
  16. }
  17. public function execute() {
  18. $this->lightBulbControl->turnOn();
  19. }
  20. }
  21. class TurnOffLightBulb implements LightBulbCommand {
  22. private $lightBulbControl;
  23. public function __construct(LightBulbControl $lightBulbControl) {
  24. $this->lightBulbControl = $lightBulbControl;
  25. }
  26. public function execute() {
  27. $this->lightBulbControl->turnOff();
  28. }
  29. }
  30. // Client
  31. $command = new TurnOffLightBulb(new LightBulbControl());
  32. $command->execute();

我们首先创建一个LightBulbCommand接口。然后我们定义了LightBulbControl类,它提供了两个简单的turnOn/turnOff方法。然后,我们定义了TurnOnLightBulbTurnOffLightBulb类,它们实现了LightBulbCommand接口。最后,客户端是用LightBulbControl的实例实例化TurnOffLightBulb对象,并对其调用执行方法。

解析器模式

解释器模式规定了如何评价语言语法或表达式。我们在定义解释器的同时,也定义了语言语法的表示方法。语言语法的表示使用复合类层次结构,其中规则被映射到类。然后,解释器使用表示法来解释语言中的表达式。

下面是一个解释器模式实现的例子。

  1. interface MathExpression
  2. {
  3. public function interpret(array $values);
  4. }
  5. class Variable implements MathExpression {
  6. private $char;
  7. public function __construct($char) {
  8. $this->char = $char;
  9. }
  10. public function interpret(array $values) {
  11. return $values[$this->char];
  12. }
  13. }
  14. class Literal implements MathExpression {
  15. private $value;
  16. public function __construct($value) {
  17. $this->value = $value;
  18. }
  19. public function interpret(array $values) {
  20. return $this->value;
  21. }
  22. }
  23. class Sum implements MathExpression {
  24. private $x;
  25. private $y;
  26. public function __construct(MathExpression $x, MathExpression $y) {
  27. $this->x = $x;
  28. $this->y = $y;
  29. }
  30. public function interpret(array $values) {
  31. return $this->x->interpret($values) + $this->y->interpret($values);
  32. }
  33. }
  34. class Product implements MathExpression {
  35. private $x;
  36. private $y;
  37. public function __construct(MathExpression $x, MathExpression $y) {
  38. $this->x = $x;
  39. $this->y = $y;
  40. }
  41. public function interpret(array $values) {
  42. return $this->x->interpret($values) * $this->y->interpret($values);
  43. }
  44. }
  45. // Client
  46. $expression = new Product(
  47. new Literal(5),
  48. new Sum(
  49. new Variable('c'),
  50. new Literal(2)
  51. )
  52. );
  53. echo $expression->interpret(array('c' => 3)); // 25

我们首先创建一个MathExpression接口,有一个 interpret 方法。然后我们添加VariableLiteralSumProduct类,所有这些类都实现了MathExpression接口。然后,客户端从Product类中实例化,将LiteralSum的实例传递给它,并以一个 interpret 方法调用结束。

迭代器模式

迭代器模式用于遍历一个容器并访问其元素。换句话说,一个类可以遍历另一个类的元素。PHP对迭代器有一个本地支持,作为内置的\Iterator\IteratorAggregate接口的一部分。

下面是一个迭代器模式实现的例子。

  1. class ProductIterator implements \Iterator {
  2. private $position = 0;
  3. private $productsCollection;
  4. public function __construct(ProductCollection $productsCollection) {
  5. $this->productsCollection = $productsCollection;
  6. }
  7. public function current() {
  8. return $this->productsCollection->getProduct($this->position);
  9. }
  10. public function key() {
  11. return $this->position;
  12. }
  13. public function next() {
  14. $this->position++;
  15. }
  16. public function rewind() {
  17. $this->position = 0;
  18. }
  19. public function valid() {
  20. return !is_null($this->productsCollection->getProduct($this->position));
  21. }
  22. }
  23. class ProductCollection implements \IteratorAggregate {
  24. private $products = array();
  25. public function getIterator() {
  26. return new ProductIterator($this);
  27. }
  28. public function addProduct($string) {
  29. $this->products[] = $string;
  30. }
  31. public function getProduct($key) {
  32. if (isset($this->products[$key])) {
  33. return $this->products[$key];
  34. }
  35. return null;
  36. }
  37. public function isEmpty() {
  38. return empty($products);
  39. }
  40. }
  41. $products = new ProductCollection();
  42. $products->addProduct('T-Shirt Red');
  43. $products->addProduct('T-Shirt Blue');
  44. $products->addProduct('T-Shirt Green');
  45. $products->addProduct('T-Shirt Yellow');
  46. foreach ($products as $product) {
  47. var_dump($product);
  48. }

首先创建了一个ProductIterator,它实现了标准的PHP \Iterator接口,然后我们添加了ProductCollection,它实现了标准的PHP \IteratorAggregate接口。客户端创建了一个ProductCollection的实例,通过addProduct方法调用将值堆积到其中,并循环浏览整个集合。

中介者模式

我们的软件中的类越多,它们的通信就越复杂。中介者模式通过将其封装成一个中介者对象来解决这种复杂性。对象不再直接通信,而是通过中介对象进行通信,因此降低了整体的耦合度。

下面是一个中介者模式实现的例子。

  1. interface MediatorInterface {
  2. public function fight();
  3. public function talk();
  4. public function registerA(ColleagueA $a);
  5. public function registerB(ColleagueB $b);
  6. }
  7. class ConcreteMediator implements MediatorInterface {
  8. protected $talk; // ColleagueA
  9. protected $fight; // ColleagueB
  10. public function registerA(ColleagueA $a) {
  11. $this->talk = $a;
  12. }
  13. public function registerB(ColleagueB $b) {
  14. $this->fight = $b;
  15. }
  16. public function fight() {
  17. echo 'fighting...';
  18. }
  19. public function talk() {
  20. echo 'talking...';
  21. }
  22. }
  23. abstract class Colleague {
  24. protected $mediator; // MediatorInterface
  25. public abstract function doSomething();
  26. }
  27. class ColleagueA extends Colleague {
  28. public function __construct(MediatorInterface $mediator) {
  29. $this->mediator = $mediator;
  30. $this->mediator->registerA($this);
  31. }
  32. public function doSomething() {
  33. $this->mediator->talk();
  34. }
  35. }
  36. class ColleagueB extends Colleague {
  37. public function __construct(MediatorInterface $mediator) {
  38. $this->mediator = $mediator;
  39. $this->mediator->registerB($this);
  40. }
  41. public function doSomething() {
  42. $this->mediator->fight();
  43. }
  44. }
  45. // Client
  46. $mediator = new ConcreteMediator();
  47. $talkColleague = new ColleagueA($mediator);
  48. $fightColleague = new ColleagueB($mediator);
  49. $talkColleague->doSomething();
  50. $fightColleague->doSomething();

我们首先创建了一个带有多个方法的MediatorInterface,由ConcreteMediator类实现。然后,我们定义了抽象类Colleague,强制在下面的ColleagueAColleagueB类上实现doSomething方法。客户端首先实例化ConcreteMediator,并将其实例传递给ColleagueAColleagueB的实例,并根据这些实例调用doSomething方法。

备忘录模式

备忘录模式提供了对象还原功能。通过三个不同的对象来实现:originator、caretaker和memento,其中originator是保存以后还原所需的内部状态的对象。

以下是备忘录模式实现的一个例子。

  1. class Memento {
  2. private $state;
  3. public function __construct($state) {
  4. $this->state = $state;
  5. }
  6. public function getState() {
  7. return $this->state;
  8. }
  9. }
  10. class Originator {
  11. private $state;
  12. public function setState($state) {
  13. return $this->state = $state;
  14. }
  15. public function getState() {
  16. return $this->state;
  17. }
  18. public function saveToMemento() {
  19. return new Memento($this->state);
  20. }
  21. public function restoreFromMemento(Memento $memento) {
  22. $this->state = $memento->getState();
  23. }
  24. }
  25. // Client - Caretaker
  26. $savedStates = array();
  27. $originator = new Originator();
  28. $originator->setState('new');
  29. $originator->setState('pending');
  30. $savedStates[] = $originator->saveToMemento();
  31. $originator->setState('processing');
  32. $savedStates[] = $originator->saveToMemento();
  33. $originator->setState('complete');
  34. $originator->restoreFromMemento($savedStates[1]);
  35. echo $originator->getState(); // processing

我们首先创建一个Memento类,它将通过getState方法提供对象的当前状态。然后我们定义了Originator类,将状态推送给Memento。最后,客户端通过实例化Originator来扮演 caretaker 的角色,在它的几个状态之间进行杂耍,从Memento中保存和恢复这些状态。

观察者模式

观察者模式实现了对象之间一对多的依赖关系。持有依赖列表的对象被称为subject,而依赖者被称为observer。当subject对象改变状态时,所有的依赖对象都会得到通知并自动更新。

下面是一个观察者模式的实现例子。

  1. class Customer implements \SplSubject {
  2. protected $data = array();
  3. protected $observers = array();
  4. public function attach(\SplObserver $observer) {
  5. $this->observers[] = $observer;
  6. }
  7. public function detach(\SplObserver $observer) {
  8. $index = array_search($observer, $this->observers);
  9. if ($index !== false) {
  10. unset($this->observers[$index]);
  11. }
  12. }
  13. public function notify() {
  14. foreach ($this->observers as $observer) {
  15. $observer->update($this);
  16. echo 'observer updated';
  17. }
  18. }
  19. public function __set($name, $value) {
  20. $this->data[$name] = $value;
  21. // notify the observers, that user has been updated
  22. $this->notify();
  23. }
  24. }
  25. class CustomerObserver implements \SplObserver {
  26. public function update(\SplSubject $subject) {
  27. /* Implementation... */
  28. }
  29. }
  30. // Client
  31. $user = new Customer();
  32. $customerObserver = new CustomerObserver();
  33. $user->attach($customerObserver);
  34. $user->name = 'John Doe';
  35. $user->email = 'john.doe@fake.mail';

我们首先创建一个Customer类,它实现了标准的PHP \SplSubject接口。然后我们定义了CustomerObserver类,它实现了标准的PHP \SplObserver接口。最后,客户端实例化CustomerCustomerObserver对象,并将CustomerObserver对象附加到Customer上。任何对姓名和电子邮件属性的改变都会被观察者捕获。

状态模式

状态模式封装了同一对象根据其内部状态而产生的不同行为,使对象看起来像是改变了它的类。

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

  1. interface Statelike {
  2. public function writeName(StateContext $context, $name);
  3. }
  4. class StateLowerCase implements Statelike {
  5. public function writeName(StateContext $context, $name) {
  6. echo strtolower($name);
  7. $context->setState(new StateMultipleUpperCase());
  8. }
  9. }
  10. class StateMultipleUpperCase implements Statelike {
  11. private $count = 0;
  12. public function writeName(StateContext $context, $name) {
  13. $this->count++;
  14. echo strtoupper($name);
  15. /* Change state after two invocations */
  16. if ($this->count > 1) {
  17. $context->setState(new StateLowerCase());
  18. }
  19. }
  20. }
  21. class StateContext {
  22. private $state;
  23. public function setState(Statelike $state) {
  24. $this->state = $state;
  25. }
  26. public function writeName($name) {
  27. $this->state->writeName($this, $name);
  28. }
  29. }
  30. // Client
  31. $stateContext = new StateContext();
  32. $stateContext->setState(new StateLowerCase());
  33. $stateContext->writeName('Monday');
  34. $stateContext->writeName('Tuesday');
  35. $stateContext->writeName('Wednesday');
  36. $stateContext->writeName('Thursday');
  37. $stateContext->writeName('Friday');
  38. $stateContext->writeName('Saturday');
  39. $stateContext->writeName('Sunday');

我们首先创建了一个Statelike接口,然后是实现该接口的StateLowerCaseStateMultipleUpperCaseStateMultipleUpperCase在它的writeName中加入了一点计数逻辑,所以它在两次调用之后就会启动新的状态。然后我们定义了StateContext类,我们将用它来切换上下文。最后,客户端实例化StateContext,并通过setState方法将StateLowerCase的一个实例传递给它,然后是几个writeName方法。

策略模式

策略模式定义了一个算法家族,每个算法都被封装起来,并可与该家族中的其他成员互换。

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

  1. interface PaymentStrategy {
  2. public function pay($amount);
  3. }
  4. class StripePayment implements PaymentStrategy {
  5. public function pay($amount) {
  6. echo 'StripePayment...';
  7. }
  8. }
  9. class PayPalPayment implements PaymentStrategy {
  10. public function pay($amount) {
  11. echo 'PayPalPayment...';
  12. }
  13. }
  14. class Checkout {
  15. private $amount = 0;
  16. public function __construct($amount = 0) {
  17. $this->amount = $amount;
  18. }
  19. public function capturePayment() {
  20. if ($this->amount > 99.99) {
  21. $payment = new PayPalPayment();
  22. } else {
  23. $payment = new StripePayment();
  24. }
  25. $payment->pay($this->amount);
  26. }
  27. }
  28. $checkout = new Checkout(49.99);
  29. $checkout->capturePayment(); // StripePayment...
  30. $checkout = new Checkout(199.99);
  31. $checkout->capturePayment(); // PayPalPayment...

我们首先创建了一个PaymentStrategy接口,然后是实现它的具体类StripePaymentPayPalPayment。然后我们定义了Checkout类,并在capturePayment方法中加入了一些决策逻辑。最后,客户端实例化Checkout,通过其构造函数传递一定的金额。根据金额,Checkout内部在调用capturePayment时,会触发一个或另一个付款。

模板方法模式

模板方法模式在方法中定义了算法的程序骨架。它让我们通过使用类重载,重新定义算法的某些步骤,而不真正改变算法的结构。

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

  1. abstract class Game {
  2. private $playersCount;
  3. abstract function initializeGame();
  4. abstract function makePlay($player);
  5. abstract function endOfGame();
  6. abstract function printWinner();
  7. public function playOneGame($playersCount)
  8. {
  9. $this->playersCount = $playersCount;
  10. $this->initializeGame();
  11. $j = 0;
  12. while (!$this->endOfGame()) {
  13. $this->makePlay($j);
  14. $j = ($j + 1) % $playersCount;
  15. }
  16. $this->printWinner();
  17. }
  18. }
  19. class Monopoly extends Game {
  20. public function initializeGame() {
  21. // Implementation...
  22. }
  23. public function makePlay($player) {
  24. // Implementation...
  25. }
  26. public function endOfGame() {
  27. // Implementation...
  28. }
  29. public function printWinner() {
  30. // Implementation...
  31. }
  32. }
  33. class Chess extends Game {
  34. public function initializeGame() {
  35. // Implementation...
  36. }
  37. public function makePlay($player) {
  38. // Implementation...
  39. }
  40. public function endOfGame() {
  41. // Implementation...
  42. }
  43. public function printWinner() {
  44. // Implementation...
  45. }
  46. }
  47. $game = new Chess();
  48. $game->playOneGame(2);
  49. $game = new Monopoly();
  50. $game->playOneGame(4);

我们首先创建了一个抽象的Game类,该类提供了所有封装游戏玩法的实际抽象方法。然后我们定义了MonopolyChess类,这两个类都是由Game类扩展而来,为每个类实现了特定的游戏方法game-play。客户端只需实例化MonopolyChess对象,对每个对象调用playOneGame方法。

访问者模式

访问者模式是一种将算法与其操作的对象结构分离的方法。因此,我们能够在现有的对象结构上添加新的操作,而不实际修改这些结构。

下面是一个访问者模式的实现例子。

  1. interface RoleVisitorInterface {
  2. public function visitUser(User $role);
  3. public function visitGroup(Group $role);
  4. }
  5. class RolePrintVisitor implements RoleVisitorInterface {
  6. public function visitGroup(Group $role) {
  7. echo 'Role: ' . $role->getName();
  8. }
  9. public function visitUser(User $role) {
  10. echo 'Role: ' . $role->getName();
  11. }
  12. }
  13. abstract class Role {
  14. public function accept(RoleVisitorInterface $visitor) {
  15. $klass = get_called_class();
  16. preg_match('#([^\\\\]+)$#', $klass, $extract);
  17. $visitingMethod = 'visit' . $extract[1];
  18. if (!method_exists(__NAMESPACE__ . '\RoleVisitorInterface', $visitingMethod)) {
  19. throw new \InvalidArgumentException("The visitor you provide cannot visit a $klass instance");
  20. }
  21. call_user_func(array($visitor, $visitingMethod), $this);
  22. }
  23. }
  24. class User extends Role {
  25. protected $name;
  26. public function __construct($name) {
  27. $this->name = (string)$name;
  28. }
  29. public function getName() {
  30. return 'User ' . $this->name;
  31. }
  32. }
  33. class Group extends Role {
  34. protected $name;
  35. public function __construct($name) {
  36. $this->name = (string)$name;
  37. }
  38. public function getName() {
  39. return 'Group: ' . $this->name;
  40. }
  41. }
  42. $group = new Group('my group');
  43. $user = new User('my user');
  44. $visitor = new RolePrintVisitor;
  45. $group->accept($visitor);
  46. $user->accept($visitor);

我们首先创建一个RoleVisitorInterface,然后是RolePrintVisitor,它实现了RoleVisitorInterface本身。然后我们定义了一个抽象类Role,它有一个接受方法来接受RoleVisitorInterface的参数类型。我们进一步定义了具体的UserGroup类,这两个类都是从Role扩展而来的。客户端实例化UserGroupRolePrintVisitor;将 visitor 传入UserGroup实例的accept方法调用。