行为型解决的是不同对象之间的通信挑战。它们描述了不同的对象和类之间如何相互发送消息以使事情发生。以下是我们归类为行为型的模式列表。
责任链模式
责任链模式通过使一个以上的对象以链式方式处理请求,将请求的发送方与接收方解耦。各种类型的处理对象可以动态地添加到链中。使用递归组成链,可以实现无限数量的处理对象。
下面是一个责任链模式的实现例子。
abstract class SocialNotifier {
private $notifyNext = null;
public function notifyNext(SocialNotifier $notifier) {
$this->notifyNext = $notifier;
return $this->notifyNext;
}
final public function push($message) {
$this->publish($message);
if ($this->notifyNext !== null) {
$this->notifyNext->push($message);
}
}
abstract protected function publish($message);
}
class TwitterSocialNotifier extends SocialNotifier {
public function publish($message) {
// Implementation...
}
}
class FacebookSocialNotifier extends SocialNotifier {
protected function publish($message) {
// Implementation...
}
}
class PinterestSocialNotifier extends SocialNotifier {
protected function publish($message) {
// Implementation...
}
}
// Client
$notifier = new TwitterSocialNotifier();
$notifier->notifyNext(new FacebookSocialNotifier())
->notifyNext(new PinterestSocialNotifier());
$notifier->push('Awesome new product available!');
我们首先创建了一个抽象的SocialNotifier
类,并实现了抽象方法publish
、notifyNext
和push
方法。然后我们定义了TwitterSocialNotifier
、FacebookSocialNotifier
和PinterestSocialNotifier
,它们都是对抽象SocialNotifier
的扩展。客户端首先实例化TwitterSocialNotifier
,然后是两次notifyNext
调用,在调用最后的push
方法之前,向它传递另外两个notifier
类型的实例。
命令模式
命令模式将执行某些操作的对象与知道如何使用它的对象解耦。它通过封装以后执行某个操作所需的所有相关信息来实现。这意味着对象、方法名和方法参数的信息。
下面是一个命令模式的实现例子。
interface LightBulbCommand {
public function execute();
}
class LightBulbControl {
public function turnOn() {
echo 'LightBulb turnOn';
}
public function turnOff() {
echo 'LightBulb turnOff';
}
}
class TurnOnLightBulb implements LightBulbCommand {
private $lightBulbControl;
public function __construct(LightBulbControl $lightBulbControl) {
$this->lightBulbControl = $lightBulbControl;
}
public function execute() {
$this->lightBulbControl->turnOn();
}
}
class TurnOffLightBulb implements LightBulbCommand {
private $lightBulbControl;
public function __construct(LightBulbControl $lightBulbControl) {
$this->lightBulbControl = $lightBulbControl;
}
public function execute() {
$this->lightBulbControl->turnOff();
}
}
// Client
$command = new TurnOffLightBulb(new LightBulbControl());
$command->execute();
我们首先创建一个LightBulbCommand
接口。然后我们定义了LightBulbControl
类,它提供了两个简单的turnOn
/turnOff
方法。然后,我们定义了TurnOnLightBulb
和TurnOffLightBulb
类,它们实现了LightBulbCommand
接口。最后,客户端是用LightBulbControl
的实例实例化TurnOffLightBulb
对象,并对其调用执行方法。
解析器模式
解释器模式规定了如何评价语言语法或表达式。我们在定义解释器的同时,也定义了语言语法的表示方法。语言语法的表示使用复合类层次结构,其中规则被映射到类。然后,解释器使用表示法来解释语言中的表达式。
下面是一个解释器模式实现的例子。
interface MathExpression
{
public function interpret(array $values);
}
class Variable implements MathExpression {
private $char;
public function __construct($char) {
$this->char = $char;
}
public function interpret(array $values) {
return $values[$this->char];
}
}
class Literal implements MathExpression {
private $value;
public function __construct($value) {
$this->value = $value;
}
public function interpret(array $values) {
return $this->value;
}
}
class Sum implements MathExpression {
private $x;
private $y;
public function __construct(MathExpression $x, MathExpression $y) {
$this->x = $x;
$this->y = $y;
}
public function interpret(array $values) {
return $this->x->interpret($values) + $this->y->interpret($values);
}
}
class Product implements MathExpression {
private $x;
private $y;
public function __construct(MathExpression $x, MathExpression $y) {
$this->x = $x;
$this->y = $y;
}
public function interpret(array $values) {
return $this->x->interpret($values) * $this->y->interpret($values);
}
}
// Client
$expression = new Product(
new Literal(5),
new Sum(
new Variable('c'),
new Literal(2)
)
);
echo $expression->interpret(array('c' => 3)); // 25
我们首先创建一个MathExpression
接口,有一个 interpret
方法。然后我们添加Variable
、Literal
、Sum
和Product
类,所有这些类都实现了MathExpression
接口。然后,客户端从Product
类中实例化,将Literal
和Sum
的实例传递给它,并以一个 interpret
方法调用结束。
迭代器模式
迭代器模式用于遍历一个容器并访问其元素。换句话说,一个类可以遍历另一个类的元素。PHP对迭代器有一个本地支持,作为内置的\Iterator
和\IteratorAggregate
接口的一部分。
下面是一个迭代器模式实现的例子。
class ProductIterator implements \Iterator {
private $position = 0;
private $productsCollection;
public function __construct(ProductCollection $productsCollection) {
$this->productsCollection = $productsCollection;
}
public function current() {
return $this->productsCollection->getProduct($this->position);
}
public function key() {
return $this->position;
}
public function next() {
$this->position++;
}
public function rewind() {
$this->position = 0;
}
public function valid() {
return !is_null($this->productsCollection->getProduct($this->position));
}
}
class ProductCollection implements \IteratorAggregate {
private $products = array();
public function getIterator() {
return new ProductIterator($this);
}
public function addProduct($string) {
$this->products[] = $string;
}
public function getProduct($key) {
if (isset($this->products[$key])) {
return $this->products[$key];
}
return null;
}
public function isEmpty() {
return empty($products);
}
}
$products = new ProductCollection();
$products->addProduct('T-Shirt Red');
$products->addProduct('T-Shirt Blue');
$products->addProduct('T-Shirt Green');
$products->addProduct('T-Shirt Yellow');
foreach ($products as $product) {
var_dump($product);
}
首先创建了一个ProductIterator
,它实现了标准的PHP \Iterator
接口,然后我们添加了ProductCollection
,它实现了标准的PHP \IteratorAggregate
接口。客户端创建了一个ProductCollection
的实例,通过addProduct
方法调用将值堆积到其中,并循环浏览整个集合。
中介者模式
我们的软件中的类越多,它们的通信就越复杂。中介者模式通过将其封装成一个中介者对象来解决这种复杂性。对象不再直接通信,而是通过中介对象进行通信,因此降低了整体的耦合度。
下面是一个中介者模式实现的例子。
interface MediatorInterface {
public function fight();
public function talk();
public function registerA(ColleagueA $a);
public function registerB(ColleagueB $b);
}
class ConcreteMediator implements MediatorInterface {
protected $talk; // ColleagueA
protected $fight; // ColleagueB
public function registerA(ColleagueA $a) {
$this->talk = $a;
}
public function registerB(ColleagueB $b) {
$this->fight = $b;
}
public function fight() {
echo 'fighting...';
}
public function talk() {
echo 'talking...';
}
}
abstract class Colleague {
protected $mediator; // MediatorInterface
public abstract function doSomething();
}
class ColleagueA extends Colleague {
public function __construct(MediatorInterface $mediator) {
$this->mediator = $mediator;
$this->mediator->registerA($this);
}
public function doSomething() {
$this->mediator->talk();
}
}
class ColleagueB extends Colleague {
public function __construct(MediatorInterface $mediator) {
$this->mediator = $mediator;
$this->mediator->registerB($this);
}
public function doSomething() {
$this->mediator->fight();
}
}
// Client
$mediator = new ConcreteMediator();
$talkColleague = new ColleagueA($mediator);
$fightColleague = new ColleagueB($mediator);
$talkColleague->doSomething();
$fightColleague->doSomething();
我们首先创建了一个带有多个方法的MediatorInterface
,由ConcreteMediator
类实现。然后,我们定义了抽象类Colleague
,强制在下面的ColleagueA
和ColleagueB
类上实现doSomething
方法。客户端首先实例化ConcreteMediator
,并将其实例传递给ColleagueA
和ColleagueB
的实例,并根据这些实例调用doSomething
方法。
备忘录模式
备忘录模式提供了对象还原功能。通过三个不同的对象来实现:originator、caretaker和memento,其中originator是保存以后还原所需的内部状态的对象。
以下是备忘录模式实现的一个例子。
class Memento {
private $state;
public function __construct($state) {
$this->state = $state;
}
public function getState() {
return $this->state;
}
}
class Originator {
private $state;
public function setState($state) {
return $this->state = $state;
}
public function getState() {
return $this->state;
}
public function saveToMemento() {
return new Memento($this->state);
}
public function restoreFromMemento(Memento $memento) {
$this->state = $memento->getState();
}
}
// Client - Caretaker
$savedStates = array();
$originator = new Originator();
$originator->setState('new');
$originator->setState('pending');
$savedStates[] = $originator->saveToMemento();
$originator->setState('processing');
$savedStates[] = $originator->saveToMemento();
$originator->setState('complete');
$originator->restoreFromMemento($savedStates[1]);
echo $originator->getState(); // processing
我们首先创建一个Memento
类,它将通过getState
方法提供对象的当前状态。然后我们定义了Originator
类,将状态推送给Memento
。最后,客户端通过实例化Originator
来扮演 caretaker
的角色,在它的几个状态之间进行杂耍,从Memento
中保存和恢复这些状态。
观察者模式
观察者模式实现了对象之间一对多的依赖关系。持有依赖列表的对象被称为subject,而依赖者被称为observer。当subject对象改变状态时,所有的依赖对象都会得到通知并自动更新。
下面是一个观察者模式的实现例子。
class Customer implements \SplSubject {
protected $data = array();
protected $observers = array();
public function attach(\SplObserver $observer) {
$this->observers[] = $observer;
}
public function detach(\SplObserver $observer) {
$index = array_search($observer, $this->observers);
if ($index !== false) {
unset($this->observers[$index]);
}
}
public function notify() {
foreach ($this->observers as $observer) {
$observer->update($this);
echo 'observer updated';
}
}
public function __set($name, $value) {
$this->data[$name] = $value;
// notify the observers, that user has been updated
$this->notify();
}
}
class CustomerObserver implements \SplObserver {
public function update(\SplSubject $subject) {
/* Implementation... */
}
}
// Client
$user = new Customer();
$customerObserver = new CustomerObserver();
$user->attach($customerObserver);
$user->name = 'John Doe';
$user->email = 'john.doe@fake.mail';
我们首先创建一个Customer
类,它实现了标准的PHP \SplSubject
接口。然后我们定义了CustomerObserver
类,它实现了标准的PHP \SplObserver
接口。最后,客户端实例化Customer
和CustomerObserver
对象,并将CustomerObserver
对象附加到Customer
上。任何对姓名和电子邮件属性的改变都会被观察者捕获。
状态模式
状态模式封装了同一对象根据其内部状态而产生的不同行为,使对象看起来像是改变了它的类。
下面是一个状态模式的实现例子。
interface Statelike {
public function writeName(StateContext $context, $name);
}
class StateLowerCase implements Statelike {
public function writeName(StateContext $context, $name) {
echo strtolower($name);
$context->setState(new StateMultipleUpperCase());
}
}
class StateMultipleUpperCase implements Statelike {
private $count = 0;
public function writeName(StateContext $context, $name) {
$this->count++;
echo strtoupper($name);
/* Change state after two invocations */
if ($this->count > 1) {
$context->setState(new StateLowerCase());
}
}
}
class StateContext {
private $state;
public function setState(Statelike $state) {
$this->state = $state;
}
public function writeName($name) {
$this->state->writeName($this, $name);
}
}
// Client
$stateContext = new StateContext();
$stateContext->setState(new StateLowerCase());
$stateContext->writeName('Monday');
$stateContext->writeName('Tuesday');
$stateContext->writeName('Wednesday');
$stateContext->writeName('Thursday');
$stateContext->writeName('Friday');
$stateContext->writeName('Saturday');
$stateContext->writeName('Sunday');
我们首先创建了一个Statelike
接口,然后是实现该接口的StateLowerCase
和StateMultipleUpperCase
。StateMultipleUpperCase
在它的writeName
中加入了一点计数逻辑,所以它在两次调用之后就会启动新的状态。然后我们定义了StateContext
类,我们将用它来切换上下文。最后,客户端实例化StateContext
,并通过setState
方法将StateLowerCase
的一个实例传递给它,然后是几个writeName
方法。
策略模式
策略模式定义了一个算法家族,每个算法都被封装起来,并可与该家族中的其他成员互换。
下面是一个策略模式的实现例子。
interface PaymentStrategy {
public function pay($amount);
}
class StripePayment implements PaymentStrategy {
public function pay($amount) {
echo 'StripePayment...';
}
}
class PayPalPayment implements PaymentStrategy {
public function pay($amount) {
echo 'PayPalPayment...';
}
}
class Checkout {
private $amount = 0;
public function __construct($amount = 0) {
$this->amount = $amount;
}
public function capturePayment() {
if ($this->amount > 99.99) {
$payment = new PayPalPayment();
} else {
$payment = new StripePayment();
}
$payment->pay($this->amount);
}
}
$checkout = new Checkout(49.99);
$checkout->capturePayment(); // StripePayment...
$checkout = new Checkout(199.99);
$checkout->capturePayment(); // PayPalPayment...
我们首先创建了一个PaymentStrategy
接口,然后是实现它的具体类StripePayment
和PayPalPayment
。然后我们定义了Checkout
类,并在capturePayment
方法中加入了一些决策逻辑。最后,客户端实例化Checkout
,通过其构造函数传递一定的金额。根据金额,Checkout
内部在调用capturePayment
时,会触发一个或另一个付款。
模板方法模式
模板方法模式在方法中定义了算法的程序骨架。它让我们通过使用类重载,重新定义算法的某些步骤,而不真正改变算法的结构。
下面是一个模板方法模式实现的例子。
abstract class Game {
private $playersCount;
abstract function initializeGame();
abstract function makePlay($player);
abstract function endOfGame();
abstract function printWinner();
public function playOneGame($playersCount)
{
$this->playersCount = $playersCount;
$this->initializeGame();
$j = 0;
while (!$this->endOfGame()) {
$this->makePlay($j);
$j = ($j + 1) % $playersCount;
}
$this->printWinner();
}
}
class Monopoly extends Game {
public function initializeGame() {
// Implementation...
}
public function makePlay($player) {
// Implementation...
}
public function endOfGame() {
// Implementation...
}
public function printWinner() {
// Implementation...
}
}
class Chess extends Game {
public function initializeGame() {
// Implementation...
}
public function makePlay($player) {
// Implementation...
}
public function endOfGame() {
// Implementation...
}
public function printWinner() {
// Implementation...
}
}
$game = new Chess();
$game->playOneGame(2);
$game = new Monopoly();
$game->playOneGame(4);
我们首先创建了一个抽象的Game
类,该类提供了所有封装游戏玩法的实际抽象方法。然后我们定义了Monopoly
和Chess
类,这两个类都是由Game
类扩展而来,为每个类实现了特定的游戏方法game-play
。客户端只需实例化Monopoly
和Chess
对象,对每个对象调用playOneGame
方法。
访问者模式
访问者模式是一种将算法与其操作的对象结构分离的方法。因此,我们能够在现有的对象结构上添加新的操作,而不实际修改这些结构。
下面是一个访问者模式的实现例子。
interface RoleVisitorInterface {
public function visitUser(User $role);
public function visitGroup(Group $role);
}
class RolePrintVisitor implements RoleVisitorInterface {
public function visitGroup(Group $role) {
echo 'Role: ' . $role->getName();
}
public function visitUser(User $role) {
echo 'Role: ' . $role->getName();
}
}
abstract class Role {
public function accept(RoleVisitorInterface $visitor) {
$klass = get_called_class();
preg_match('#([^\\\\]+)$#', $klass, $extract);
$visitingMethod = 'visit' . $extract[1];
if (!method_exists(__NAMESPACE__ . '\RoleVisitorInterface', $visitingMethod)) {
throw new \InvalidArgumentException("The visitor you provide cannot visit a $klass instance");
}
call_user_func(array($visitor, $visitingMethod), $this);
}
}
class User extends Role {
protected $name;
public function __construct($name) {
$this->name = (string)$name;
}
public function getName() {
return 'User ' . $this->name;
}
}
class Group extends Role {
protected $name;
public function __construct($name) {
$this->name = (string)$name;
}
public function getName() {
return 'Group: ' . $this->name;
}
}
$group = new Group('my group');
$user = new User('my user');
$visitor = new RolePrintVisitor;
$group->accept($visitor);
$user->accept($visitor);
我们首先创建一个RoleVisitorInterface
,然后是RolePrintVisitor
,它实现了RoleVisitorInterface
本身。然后我们定义了一个抽象类Role
,它有一个接受方法来接受RoleVisitorInterface
的参数类型。我们进一步定义了具体的User
和Group
类,这两个类都是从Role
扩展而来的。客户端实例化User
、Group
和RolePrintVisitor
;将 visitor
传入User
和Group
实例的accept
方法调用。