php 和设计模式 - 流接口模式
流接口模式通常用来编写易于阅读的代码,就像自然语言一样(如英语)。 1234567891011121314151617181920212223242526272829303132333435363738394041class Sql{ private array $fields = []; private array $from = []; private array $where = []; public function select(array $fields): Sql { $this->fields = $fields; return $this; } public function from(string $table, string $alias): Sql { $this->from[] = $table . ' AS ' . $alias; return $this; } public function where(string $condition): Sql { $this->where[] = $condition; return $this; } public function __toString(): string { return sprintf( 'select %s from %s where %s', join(', ', $this->fields), join(', ', $this->from), join(' AND ', $this->where) ); }}$query = (new Sql())->select(['foo', 'bar'])->from('foobar', 'f')->where('f.bar = foo');echo $query, PHP_EOL; 这个模式跟前一个模式都有点说不出的诡异,可能不是新模式,属于没被正式划分到设计模式中的模式???
php 和设计模式 - 依赖注入模式
依赖注入是控制反转的一种实现方式。要实现控制反转,需要将创建被调用者实例的工作交由 IOC 容器完成,然后在调用者中注入被调用者,通常使用构造器或方法注入实现。这样我们舅实现了调用者和被调用者的解偶,这个过程就是依赖注入。 那么控制反转是什么呢?其实也就是 A 依赖于 B,常规做法是在 A 中直接实例化 B,那么控制反转就是将 B 在外部实例化,然后传入 A 去使用。看完以后,其实对依赖注入也就有了理解。 12345678910111213141516171819202122232425262728class Computer{ protected HardDisk $hardDisk; public function __construct(HardDisk $disk) { $this->hardDisk = $disk; } public function run() { $this->hardDisk->run(); echo '一台没有感情的电脑开始运行', PHP_EOL; }}class HardDisk{ public function run() { echo '一块没有感情的硬盘开始运行', PHP_EOL; }}$disk = new HardDisk();$computer = new Computer($disk);$computer->run(); 以上代码就是一个简单的依赖注入,你以为这就结束了?并没有,咱们在学一下 IOC 容器: 1234567891011121314151617181920212223242526272829class Container{ public array $bindings = []; public function bind($key, Closure $value) { $this->bindings[$key] = $value; } public function make($key) { $new = $this->bindings[$key]; return $new(); }}$container = new Container();$container->bind('disk', function (){ return new HardDisk();});$container->bind('computer', function () use($container){ return new Computer($container->make('disk'));});$computer = $container->make('computer');$computer->run(); ok,以上就是依赖注入的全部代码了。
php 和设计模式 - 注册模式
注册模式通常将对象注册到一个全局的对象树上,需要的时候从对象树上取下需要的实例,就像卖糖葫芦的有木有。不过不一样的是糖葫芦会摘完,对象树摘下后并不会销毁该对象。 注册模式通常通过一个只有静态方法的抽象类来存放这些对象。或者通过单例模式。 1234567891011121314151617181920212223abstract class registry{ const LOGGER = 'logger'; public static array $objects = []; public static function set($key, $value) { self::$objects[$key] = $value; } public static function get($key) { return self::$objects[$key]; }}$key = Registry::LOGGER;$logger = new stdClass();Registry::set($key, $logger);$storedLogger = Registry::get($key);var_dump($storedLogger);
php 和设计模式 - 享元模式
享元模式会尽量使相似的对象共享内存,能让你在有限的内存中载入更多对象。 当一个应用程序需要创建大量对象,并且这些对象的大多数状都可变为外部状态时,就很适合享元模式。 一如既往的举个🌰: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889interface Message{ public function send(User $user);}class AliMessage implements Message{ // 内部状态 protected Template $template; public function __construct(Template $template) { $this->template = $template; } // user 属于外部状态 public function send(User $user) { echo 'use ', $this->template->getTemplate(), ' send msg ', 'to user ', $user->getName(), ' by ali', PHP_EOL;; }}class MessageFactory{ protected array $messages = []; public function getMessage(Template $template) { $key = md5($template->getTemplate()); if (!array_key_exists($key, $this->messages)) { echo 'message create', PHP_EOL; $this->messages[$key] = new AliMessage($template); } return $this->messages[$key]; }}class Template{ protected string $template; public function setTemplate(string $template) { $this->template = $template; } public function getTemplate(): string { return $this->template; }}class User{ protected string $name; public function setName(string $name) { $this->name = $name; } public function getName(): string { return $this->name; }}$templateA = new Template();$templateA->setTemplate('template a');$templateB = new Template();$templateB->setTemplate('template b');$userA = new User();$userA->setName('wu');$userB = new User();$userB->setName('yf');$factory = new MessageFactory();$flyweightA = $factory->getMessage($templateA);$flyweightA->send($userA);$flyweightA->send($userB);$flyweightB = $factory->getMessage($templateB);$flyweightB->send($userA); 这次来点不一样的,贴张截图帮助理解: 可以看到,在享元工厂中,一共创建了两次 message,当我们重复用一个模板发送消息时,模板作为内部状态已经被缓存了,调用的时候直接取出即可,避免了重复创建造成的资源浪费。 例子虽然不太贴切,但是看完应该也能总结出,享元模式需要依赖于一个享元工厂以及一个享元角色,也就是咱们代码中的 AliMessage 类。
php 和设计模式 - 组合模式
将对象组合成树状层次结构以表示部分-整体的层次结构,使用户对单个对象和组合对象的使用具有一致性。 两个关键词,树状,部分-整体。 举个🌰: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667interface Component{ public function render();}class Composite implements Component{ protected array $composites = []; public function add(Component $component) { $this->composites[] = $component; } public function remove(Component $component) { $position = 0; foreach ($this->composites as $composite) { if ($composite === $component) { array_splice($this->composites, $position, 1); } $position++; } } public function getChildren(int $key): Component { return $this->composites[$key]; } public function render() { foreach ($this->composites as $composite) { $composite->render(); } }}class LeafA implements Component{ public function render() { echo 'leaf a render', PHP_EOL; }}class LeafB implements Component{ public function render() { echo 'leaf b render', PHP_EOL; }}$leafA = new LeafA();$leafB = new LeafB();$composite = new Composite();$composite->add($leafA);$composite->add($leafB);$composite->render();$composite->remove($leafA);$composite->render();$composite->getChildren(0)->render(); 比较适用于树形菜单、文件和文件夹管理等,感觉场景很有限啊……
php 和设计模式 - 代理模式
说到代理这个词,首先想到的是梯子,它帮助我们解决了网络问题,但是怎么处理的,我们不关心,因为这对大多数人来说属于相对生疏的专业领域。那么代理模式也是一样的道理:为其他对象提供一种代理以控制对这个对象的访问,并允许在将请求提交给对象前后进行一些处理。 按照惯例,来个🌰: 123456789101112131415161718192021222324252627282930313233343536interface RequestInterface{ public function getRequest();}class Request implements RequestInterface{ public function getRequest() { echo 'get request', PHP_EOL; }}class Proxy implements RequestInterface{ protected Request $request; public function __construct() { $this->request = new Request(); } public function getRequest() { echo 'add log in proxy', PHP_EOL; $this->request->getRequest(); }}$proxy = new Proxy();$proxy->getRequest(); 代理模式和适配器模式的区别: 适配器模式是为了改变和适配代理类的接口 代理模式不改变所代理类接口。 代理模式和装饰模式的区别: 装饰模式是为了增强功能 代理模式是为了加以控制
php 和设计模式 - 桥接模式
桥接模式也是一个典型的单一职责模式。 在组件设计过程中,如果职责划分不够清晰,当父类发生变更,子类也需要跟着变动,要么违背开闭原则,要么导致子类数量膨胀。桥接模式,就是为了解决这个问题。 桥接模式的做法是,使抽象和实现完全分离,使其能够独立变化。或者也可以直白一点,通过组合/聚合的方式避免继承滥用。 举个🌰: 123456789101112131415161718192021222324252627282930313233343536abstract class Shape{ protected Color $color; public function setColor(Color $color) { $this->color = $color; } public abstract function draw();}class Circle extends Shape{ public function draw() { $this->color->setColor(); echo 'circle', PHP_EOL; }}interface Color{ public function setColor();}class Blue implements Color{ public function setColor() { echo 'blue', PHP_EOL; }}$shape = new Circle();$shape->setColor(new Blue());$shape->draw(); 抽象部分使用继承,实现部分使用组合。 后续如果我们需要换成另外一个颜色,只需要稍作改动即可实现: 12345678910class Red implements Color{ public function setColor() { echo 'red', PHP_EOL; }}$shape = new Circle();$shape->setColor(new Red());$shape->draw();
php 和设计模式 - 装饰器模式
装饰器模式主要用于动态添加修改类的功能。 一般情况下,一个类提供了某些功能,如果要扩展或修改该类,我们可以扩展一个子类出来。但是装饰器模式可以使我们更为灵活的实现。 那么,装饰器模式相对继承灵活在哪儿呢? 举个🌰,我们有一个发送短信的类,现在要在发送短信前增加一些校验,发送短信后我们要记录 log: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374interface SendSms{ public function Send();}interface Decorator{ public function beforeSend(); public function afterSend();}class SmsDecorator implements Decorator{ public function beforeSend() { echo 'check', PHP_EOL; } public function afterSend() { echo 'log', PHP_EOL; }}class AuthSms implements SendSms{ protected $decorators = []; public function addDecorator(Decorator $decorator) { array_push($this->decorators, $decorator); } protected function beforeSend() { /** * @var Decorator $decorator */ foreach ($this->decorators as $decorator) { $decorator->beforeSend(); } } protected function afterSend() { $decorators = array_reverse($this->decorators); /** * @var Decorator $decorator */ foreach ($decorators as $decorator) { $decorator->afterSend(); } } public function Send() { $this->beforeSend(); echo 'auth sms is send', PHP_EOL; $this->afterSend(); }}$sms = new AuthSms();$sms->addDecorator(new SmsDecorator());$sms->send();
php 和设计模式 - 适配器例模式
适配器模式主要用于将一个类的接口转换为客户端所期望的另一个接口,也就是处理接口的兼容问题。 比如说数据库操作,有 mysql,sqllite,mongodb 等,缓存操作有 redis,memcache,file 等,都可以通过适配器模式将其统一成一致。 查阅资料的过程中,看到一个很生动的例子:🌰 某公司生产一批动物形玩具,可以张嘴闭嘴,实现如下: 1234567891011121314151617181920interface Toy{ public function openMouse(); public function closeMouse();}class Dog implements Toy{ public function openMouse() { echo 'dog open mouse', PHP_EOL; } public function closeMouse() { echo 'dog close mouse', PHP_EOL; }}$dog = new Dog();$dog->openMouse(); 某一天,该公司决定与另外一家公司合作,因为该公司的玩具可以遥控控制张嘴闭嘴,但是合作公司用的是 doOpenMouse 和 doCloseMouse 两个方法来控制玩具,这个时候,开发人员怎么办呢,直接在接口添加两个新方法?那岂不是违背了开闭原则,而且两组方法的功能高度重合,以后岂不是很难维护。 所以,适配器模式就可以大展身手了。 123456789101112131415161718192021222324252627282930313233343536373839404142interface RemoteControlToy{ public function doOpenMouse(); public function doCloseMouse();}class RemoteControlDog implements RemoteControlToy{ public function doOpenMouse() { echo 'remote control dog open mouse', PHP_EOL; } public function doCloseMouse() { echo 'remote control dog close mouse', PHP_EOL; }}class RemoteControllerToyAdapter implements Toy{ protected RemoteControlToy $adapter; public function __construct(RemoteControlToy $target) { $this->adapter = $target; } public function openMouse() { $this->adapter->doOpenMouse(); } public function closeMouse() { $this->adapter->doCloseMouse(); }}$adapterDog = new RemoteControllerToyAdapter(new RemoteControlDog());$adapterDog->openMouse(); 首先,我们定义并实现了遥控玩具接口,然后通过遥控玩具适配器进行接口转换,完成了接口统一。 那么像参考例子中提到的,如果这时候再来一个通过传入参数控制玩具张嘴的公司,只需要再添加一个适配器即可。
php 和设计模式 - 生成器模式
生成器模式也叫建造者模式,主要用于将一个复杂对象的构造与它的表示分离。该模式允许你使用相同的代码生成不同类型和形式的对象。 什么是复杂对象呢?举个🌰,人类,都有个脑袋,有个身体,又有两条胳膊腿儿,那么,我们就可以把人看作是一个复杂对象。 那么,对于生成器模式来说,我们要把人类对象的创建与它的实例表示进行分离。 class Human { public function setHead(string $head) { echo 'head:', $head, PHP_EOL; } public function setBody(string $body) { echo 'body:', $body, PHP_EOL; } public function setArms(string $leftArm, string $rightArm) { echo 'left arm:', $leftArm, ' right arm:', $rightArm, PHP_EOL; } } interface Builder { public function buildHead(); public function buildBody(); public function buildArms(); public function getResult(): Human; } class HumanBuilder implements Builder{ private Human $human; public function __construct() { $this->human = new Human(); } public function buildHead() { $this->human->setHead('ai'); } public function buildBody() { $this->human->setBody('body'); } public function buildArms() { $this->human->setArms('left', 'right'); } public function getResult(): Human { return $this->human; } } class Director{ public function builder(Builder $builder): Human { $builder->buildHead(); $builder->buildBody(); $builder->buildArms(); return $builder->getResult(); } } $director = new Director(); $human = $director->builder(new HumanBuilder()); 好了,生成器模式到此结束。
