php 和设计模式 - 中介者模式
最近太忙,拖更好多天,难受…… 废话不多说,先来看看概念:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地互相引用,从而使其耦合松散,而且可以肚里地改变它们之间的交互。 也就相当于你租了个房子,但是房东常年旅居国外,有事情你也不需要找房东,因为房东把所有事情都委托给了中介。 令人羡慕的房东啊…… 举个🌰: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990<?php// 中介者模式interface MediatorInterface{ public function send($service, string $message);}class Mediator implements MediatorInterface{ public function send($service, string $message) { $service->notify($message); }}abstract class Colleague{ private MediatorInterface $mediator; protected Colleague $colleague; public function setColleague(Colleague $colleague) { $this->colleague = $colleague; } public function getColleague(): Colleague { return $this->colleague; } public function setMediator(MediatorInterface $mediator) { $this->mediator = $mediator; } public function getMediator(): MediatorInterface { return $this->mediator; } public abstract function notify(string $message);}class ClientA extends Colleague{ public function send($message) { return $this->getMediator()->send($this, $message); } public function notify(string $message) { echo get_class($this->getColleague()), ' 收到消息:', $message, PHP_EOL; }}class ClientB extends Colleague{ public function send($message) { return $this->getMediator()->send($this, $message); } public function notify(string $message) { echo get_class($this->getColleague()), ' 收到消息:', $message, PHP_EOL; }}$mediator = new Mediator();$clientA = new ClientA();$clientB = new ClientB();$clientA->setMediator($mediator);$clientB->setMediator($mediator);$clientA->setColleague($clientB);$clientB->setColleague($clientA);$clientA->send('吃饭了没?');$clientB->send('没呢,要请客?'); 这个模式比较适用于通讯类产品,聊天啊、直播什么的,可以实现用户与用户之间的结偶,不需要让一个用户去维护所有管理的用户对象,但是同时也存在一些问题,比如当业务逻辑更加复杂时,中介类就会更加复杂和庞大,所以应用的同时也要考虑该如何取舍。 ok,以上就是终结者模式了,代码还是比较通透的。
php 和设计模式 - 访问者模式
这个模式就比较复杂,首先从概念来说,就是将对象的操作外包给其他对象,也就是访问者,从而实现在不改变个元素的前提下定义作用于这些元素的新操作。 当一个基类可以被访问,并具有名为 acceptVisitor 的公共方法,改方法接受参数 Visitor,然后根据传递 Visitor 对象调用公共方法 visit。 1234567891011121314151617181920212223242526272829303132333435363738394041interface Visitor{ public function visit($visitor);}class Element{ protected Visitor $visitor; public string $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } public function acceptVisitor(Visitor $visitor) { $visitor->visit($this); }}class NameVisitor implements Visitor{ public function visit($visitor) { echo $visitor->getName(), PHP_EOL; }}$element = new Element('wu');echo $element->getName(), PHP_EOL;$element->acceptVisitor(new NameVisitor()); 我们可以看到,通过调用 acceptVisitor 方法接收一个访问者,具体对象可以把访问者的getName 能力也扩展为自己能力。当然如果你需要多个扩展能力,你可以有多个访问者。而 acceptVisitor 方法调用访问者的visit 方法时,传入 $this 是为了能使用 Element 的属性和方法,使其感觉扩展完就是 Element 的真正一部分。
php 和设计模式 - 状态模式
通过改变类的状态来实现对它行为的切换。 看代码还是比较好理解的: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869abstract class State{ protected string $state; public function getState(): string { return $this->state; } abstract public function handle();}class StateContext{ protected State $state; public function setState(State $state) { $this->state = $state; } public function getState(): string { return $this->state->getState(); } public function handle() { $this->state->handle(); }}class CreateOrder extends State{ public function __construct() { $this->state = 'create'; } public function handle() { echo '创建订单', PHP_EOL; }}class FinishOrder extends State{ public function __construct() { $this->state = 'finish'; } public function handle() { echo '结束订单', PHP_EOL; }}$stateContext = new StateContext();$stateContext->setState(new CreateOrder());$stateContext->handle();echo $stateContext->getState(), PHP_EOL;$stateContext->setState(new FinishOrder());$stateContext->handle();echo $stateContext->getState(), PHP_EOL; 将状态独立,然后在外部控制状态切换,已实现对其行为控制。
php 和设计模式 - 备忘录模式
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存该对象的内部状态。这样就可以方便的恢复到之前保存的状态。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849class Memento{ protected string $state; public function __construct(string $state) { $this->state = $state; } public function getState(): string { return $this->state; }}class Originator{ protected Memento $memento; protected string $state; public function setMemento(Memento $memento): Memento { $this->memento = $memento; return $this->memento; } public function getMemento(): Memento { return $this->memento; } public function getState(): string { $this->state = $this->memento->getState(); return $this->state; }}$state = 'new state';$originator = new Originator();$memento = new Memento('memento state');echo $memento->getState(), PHP_EOL;$originator->setMemento($memento);echo $originator->getState(), PHP_EOL; 好了,以上就是一个简易版的备忘录模式了,还是比较好理解的。
php 和设计模式 - 命令行模式
用过 laravel 框架的应该都知道,其脚本模块非常强大,这些脚本,也就是命令行模式。 说到命令行,就不得步提一下 cli 和 cgi 的区别,在 nginx 中,php 并不是直接执行的,而是通过 cgi 调用 php 并获取执行结果。 而 cli 就是命令行接口,主要用于 shell 脚本的开发。 123456789php command.php/opt/homebrew/var/www/design-patternphp-cli command.phpX-Powered-By: PHP/7.4.16Content-type: text/html; charset=UTF-8/opt/homebrew/var/www/design-pattern 不多说了,回头再专门看一下这方面的东西。
php 和设计模式 - 责任链模式
建立一个对象链来按指定顺序处理调用。如果其中一个对象无法处理命令,它会委托这个调用给它的下一个对象来进行处理,以此类推。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546abstract class Handler{ protected ?Handler $successor; public function __construct(Handler $handler = null) { $this->successor = $handler; } abstract public function handle($request);}class HttpInNumeric extends Handler{ public function __construct(Handler $successor = null) { parent::__construct($successor); } public function handle($request) { if (is_numeric($request)) { echo '数字请求', PHP_EOL; } else { if ($this->successor) { return $this->successor->handle($request); } } }}class HttpInArray extends Handler{ public function handle($request) { echo '数组请求', PHP_EOL; }}$handler = new HttpInNumeric(new HttpInArray());$handler->handle(1);$handler->handle([1]); 还算简单,就是依次往下传递。
php 和设计模式 - 迭代器模式
提供一种方法顺序访问一个集合对象中的各种元素,而又不暴露该对象的内部表示。 foreach 的底层就是迭代器。很多编程语言都已经将其作为一个基础类库实现出来了,所以也就有了这个模式目前学习意义大于实际意义的说法。 在 php 中,内部已提供 Iterator 接口,可以直接使用。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879class Bookshelf implements \Countable, \Iterator{ protected array $books = []; protected int $current = 0; public function addBook(Book $book) { $this->books[] = $book; } public function current() { return $this->books[$this->current]; } public function next() { return $this->current++; } public function key(): int { return $this->current; } public function valid(): bool { return isset($this->books[$this->current]); } public function rewind() { $this->current = 0; } public function count(): int { return count($this->books); }}class Book{ protected string $author; protected string $title; public function __construct(string $author, string $title) { $this->author = $author; $this->title = $title; } public function getAuthor(): string { return $this->author; } public function getTitle(): string { return $this->title; } public function getAuthorAndTitle(): string { return $this->getAuthor() . '-' . $this->getTitle(); }}$bookA = new Book('wu', 'php');$bookB = new Book('wu', 'redis');$bookshelf = new Bookshelf();$bookshelf->addBook($bookA);$bookshelf->addBook($bookB);foreach ($bookshelf as $book) { echo $book->getAuthorAndTitle(), PHP_EOL;} 使用起来还是比较简单的,至于如何实现就不写了。
php 和设计模式 - 观察者模式
当对象的状态发生变化时,所有依赖于它的对象都得到通知并被自动更新。它使用的是低耦合的方式。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273class DeleteUserSubject implements \SplSubject{ protected SplObjectStorage $observers; protected $data; public function __construct() { $this->observers = new \SplObjectStorage(); } public function attach(SplObserver $observer) { $this->observers->attach($observer); } public function detach(SplObserver $observer) { $this->observers->detach($observer); } public function notify() { foreach ($this->observers as $observer) { $observer->update($this); } } public function process() { $this->data = new class { public string $name = 'wu'; public function delete() { echo '用户 ', $this->name, ' 被删除', PHP_EOL; } }; $this->data->delete(); echo '开始通知关联处理:', PHP_EOL; $this->notify(); } public function getName() { return $this->data->name; }}class UserLogDeleteObserver implements \SplObserver{ protected SplSubject $subject; public function update(SplSubject $subject) { $this->subject = clone $subject; $this->deleteUserLog(); } public function deleteUserLog() { echo '删除用户', $this->subject->getName(),' 的日志', PHP_EOL; }}$subject = new DeleteUserSubject();$subject->attach(new UserLogDeleteObserver());$subject->process(); 这个模式代码稍微多一点,但是场景很经典,也很容易理解。
php 和设计模式 - 模板方法模式
这个模式是对继承的最好诠释。当子类有重复动作时,将其重复动作放入父类统一处理,这就是模板方法最简单通俗的解释。 123456789101112131415161718192021abstract class BaseController{ public function baseMethod() { echo 'base method', PHP_EOL; } public abstract function operate();}class UserController extends BaseController { public function operate() { echo 'user operate', PHP_EOL; }}$user = new UserController();$user->baseMethod();$user->operate(); 这个模式太简单了,就不多说了。
php 和设计模式 - 策略模式
定义一组算法,把它们一个个封装起来,并使它们能够快速切换。本模式使得算法可以独立于使用它的客户而变化。一般用于避免多重条件判断和在运行时进行更改。 123456789101112131415161718192021222324252627282930313233343536373839404142434445interface Strategy{ public function algorithm();}class AlgorithmA implements Strategy{ public function algorithm() { echo '算法 A', PHP_EOL; }}class AlgorithmB implements Strategy{ public function algorithm() { echo '算法 B', PHP_EOL; }}class Context{ protected Strategy $strategy; public function __construct(Strategy $strategy) { $this->strategy = $strategy; } public function callAlgorithm() { $this->strategy->algorithm(); }}$algorithmA = new AlgorithmA();$contextA = new Context($algorithmA);$contextA->callAlgorithm();$algorithmB = new AlgorithmB();$contextB = new Context($algorithmB);$contextB->callAlgorithm(); 跟工厂模式非常相似,但是策略模式属于行为型模式,并不会返回一个具体的对象,而是强调其行为。通过调用上下文将要调用的方法封装起来,客户端只要调用上下文的方法就可以了。 那么,跟工厂结合一下是不是更好呢?
