php 和设计模式 - 责任链模式
发表于|更新于|设计模式
|总字数:178|阅读时长:1分钟|浏览量:
建立一个对象链来按指定顺序处理调用。如果其中一个对象无法处理命令,它会委托这个调用给它的下一个对象来进行处理,以此类推。
1 | abstract class Handler |
还算简单,就是依次往下传递。
文章作者: m-finder
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 M-finder!
相关推荐

2021-03-21
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;} 使用起来还是比较简单的,至于如何实现就不写了。

2021-03-21
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; 将状态独立,然后在外部控制状态切换,已实现对其行为控制。

2021-03-21
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(); 这个模式代码稍微多一点,但是场景很经典,也很容易理解。

2021-03-21
php 和设计模式 - 对象
我们经常会用类描述对象,也经常会用对象描述类,但是这有碍于我们对于面向对象的理解,因为类决定了对象。 简而言之,类,是用来生成一个或多个对象的代码模板。 对象是根据类中定义的模板所构建的数据,我们通常会说对象是它的类的实例,对象的类型是由类定义的。 你可以用 class 关键字和任意类名来声明一个类,类名可以是任意数字和字母的组合,但不能以数字开头,类体必须定义在一对大括号内: 123456class Person{}$person1 = new Person();$person2 = new Person(); 通过关键字 new 去创建 Person 类的对象,在上面的代码中,创建了两个实例,它们是由同一个类创建的、具有相同类型的不同对象。 如果将类看作是一个生产用的铸模,那么对象就是用铸模生产出来的具体产品。 类属性我们可以在类中定义称为 属性 的特殊变量。属性也称为 成员变量,可以用来保存各个对象中不同的数据。 除了在声明它们时必须指定可见性关键字,成员变量与普通变量看起来非常相似。 可见性关键字为 private,protected 和 public,它们确定类成员变量能够被访问的作用域。 1234567class Person{ public string $name = 'wu';}$person = new Person();echo $person->name; 类方法类方法是类中的特殊函数,它允许对象执行任务。 方法声明与函数声明类似,但是它必须在类体内。 我们可以给它加上限定词,包括可见性关键字。 1234567891011class Person{ public string $name = 'wu'; public function getName() { return $this->name; }}$person = new Person();echo $person->getName(); 继承继承是指从基类中派生出一个或多个类的机制。 如果一个类继承自另外一个类,那么就说它是另外一个类的子类。这种关系通常用父子关系来形容。 子类派生自父类并继承来父类的特性,这些特性包括属性也包括方法。 通常,子类会在父类所提供的功能基础上增加一些新功能,因此,也可以说子类扩展了父类。 继承通常用来 解决代码重复,在一个类中提供共通功能,又能在其他类在处理一些方法调用时有所不同。 封装封装是指隐藏对象内的属性和具体实现,仅对外提供公共访问方式。 封装通过 可见性关键字 把一个对象的属性私有化,同时提供一些可以被外界访问属性的方法,如果不想被外界访问,我们大可不提供方法给外界。但如果一个类没有提供给外界访问的方法,那么这个类也就没有意义了。 这样做的好处是: 良好的封装可以减少代码耦合 类内部的结构可以自由修改 可以对成员进行更精确的控制 隐藏信息,实现细节 具体场景如 model 获取数据。 封装可以 提高灵活性。使我们更容易地修改类的内部实现,而无需修改使用了该类的客户代码,从而实现对成员变量进行更精确的控制。 多态可以理解为多种表现形式,即一个对外接口,多种内部实现。 在面向对象的理论中,多态性的一般定义为:同一个操作(函数)作用于不同的类的实例,将产生不同的执行结果。即不同类的对象接收到相同的消息时,将会得到不同的结果。 举个🌰: 12345678910111213141516171819202122232425262728293031class Light{ public function show($type) { switch($type){ case 0: echo '红色', PHP_EOL; break; case 1: echo '蓝色', PHP_EOL; break; case 2: echo '绿色', PHP_EOL; break; } }}class User{ public function openLight($type = 0) { $light = new Light(); $light->show($type); }}$user = new User();$user->openLight(); 这是一个存在弊端的实现,如果灯光颜色非常多,后期添加就会非常麻烦。 多态实现: 12345678910111213141516171819202122232425262728293031323334353637383940class Light{ function show() { echo '灯光随机', PHP_EOL; }}class BlueLight extends Light{ function show() { echo '蓝色', PHP_EOL; }}class RedLight extends Light{ function show() { echo '红色', PHP_EOL; }}class User{ function openLight(Light $light) { $light->show(); }}$user = new User();$light = new Light();$blueLight = new BlueLight();$redLight = new RedLight();$user->openLight($light);$user->openLight($blueLight);$user->openLight($redLight); 静态方法前面说过,类是生成对象的代码模板,对象是类的实例。我们可以调用对象的属性和方法。在前边的例子中,也都是通过对象调用属性和方法。 事实上,我们也可以访问类的属性和方法,这种方法和属性都是静态的,需要用关键字 static 声明: 1234567891011class Person{ public static string $name = 'wu'; public static function getName() { return self::$name; }}echo Person::getName(); 静态方法拥有类作用域,它们无法访问类的普通属性。因为这些属性是对象的。 静态属性和静态方法是在类上被调用的,而不是在对象上,因此它们也被称为 类属性 和 类方法。我们也无法在类中通过伪变量 $this 调用,而是需要通过对应的 self。 静态属性和静态方法可以使我们无需将一个对象传入另一个对象就可以访问而不需要实例。这可以使我们省去实例化对象的麻烦,从而使代码更加整洁。 常量属性有些属性是不应当被改变的,这个时候就应该用关键字 const 去声明常量属性。 与普通属性不同,常量不以 $ 开头,并且根据约定,通常用大写字母命名。 12345class Person{ const LEG_NUMBER = 2;}echo Person::LEG_NUMBER; 常量只能是基本类型的值,无法用来保存对象,并且与静态属性一样,我们需要通过类来访问常量。 抽象类抽象类无法被实例化,它的作用是为所有子类(继承自它的类)定义接口。 抽象类用关键字 abstract 声明。 1234567891011abstract class Person{ public string $name; public function getName() { return $this->name; } public abstract function setName();} 可以像在普通类中一样在抽象类中创建方法和属性,但是当实例化这个类时,就会有报错出现。因为抽象类不能被实例化。 一般情况下,抽象类至少有一个抽象方法,使用同样的关键字声明,但不能有方法体。 任何继承自抽象类的非虚子类都必须实现所有的抽象方法,否则它自己就必须被定义为抽象类。 接口抽象类允许提供一些实现,但是接口则是纯粹的模板,只提供定义功能,不能有实现。 使用关键字 interface 声明接口,其中可以有常量成员和方法的声明,但是不能有方法体。 123456789101112131415interface Person{ public function getName(): string;}class Man implements Person{ public string $name; public function getName(): string { // TODO: Implement getName() method. }} PHP 中的类只能有一个继承,但是可以同时实现多个接口。 TraitPHP 不支持多继承,一个类只能有一个父类,但是可以实现多个接口。 接口提供没有任何实现的类型,如果我们希望在继承层次中共享实现,就需要借助于 trait。 trait 是类似于类的结构,它本身不能被实例化,但是可以混合到类中,在 trait 中定义的任何方法都可以被使用它的任何类所使用。 延时静态绑定:static 关键字static 和 self 类似,区别在于前者引用的是被调用的类,而不是包含类。 1234567891011121314abstract class DomainObject{ public static function create() { return new static(); }}class User extends DomainObject(){ }$user = User::create(); 调用 User::create() 会创建一个 User 实例,而不是尝试创建 DomainObject 实例。 异常捕获1234567try{ $name = 'wu';}catch(\Exception $e){ throw $e;}finally{ echo 'finally';} 无论 catch 子句是重新抛出异常还是返回一个值,finally 子句都会执行,但如果在 try 或 catch 中调用了 die() 或 exit(),那么程序就会终止,finally 子句也就不会执行。 final 类final 类可以防止类再被继承。final 方法也无法重写。 123456final class Person{ public final function getName() { return 'wu'; }} 内部错误捕获可以在 try catch 子句中通过指定 Error 这个父类或它的子类来捕获相匹配的内部错误。 123456789try { eval('illegal code');} catch (\ParseError $e) { echo 'parse error', PHP_EOL;} catch (\Error $e) { echo 'error', PHP_EOL;} finally { echo 'finally', PHP_EOL;} 同样的, finally 在这里也可以用。 拦截器PHP 内置的拦截器方法可以拦截发送给为定义方法和属性的消息。 PHP 支持三个内置的拦截器方法。与 __construct() 相似,这些方法也会在适当的条件下自动调用。 方法 说明 __get($property) 访问未定义属性时会被调用 __set($property, $value) 对未定义属性赋值时会被调用 __isset($property) 对未定义属性调用 isset()时调用 __unset($property) 对未定义属性调用 unset()时调用 __call($method, $args) 调用未定义非静态方法时调用 __callStatic($method, $args) 调用未定义静态方法时调用 1234567891011121314151617class Person{ public function __get($property) { $method = "get{$property}"; if(method_exists($this, $method)){ return $this->$method(); } } public function getName(): string { return 'wu'; }}$person = new Person();echo $person->name; 析构方法析构方法会在类被垃圾回收前,也就是从内存中抹去前调用。可以用这个方法执行一些必要的清理工作。 析构方法和前边的拦截器都属于魔术方法,使用时应该慎重。 回调、匿名函数和闭包回调有什么作用呢?它允许程序在运行期间将与组件核心任务没有直接关系的功能插入组件。通过让组件拥有回调能力,可以赋予其他程序员在我们不知道的上下文上获得扩展程序的能力。 12345678910111213141516171819202122232425class Person{ protected $callbacks = []; protected $name = 'wu'; public function __construct() { $log = function ($person){ echo $person->name, PHP_EOL; }; $this->callbacks[] = $log; } public function getName(): string { foreach ($this->callbacks as $callback) { if(is_callable($callback)){ call_user_func($callback, $this); } } return $this->name; }} 上面的代码中,将匿名函数(闭包函数)赋值给 $log,然后将它作为参数传递给函数和方法,然后在指定的位置进行回调。 匿名函数可以引用那些声明在其父作用域中的变量,通过 use() 操作。 匿名类当需要从很小的类中创建和继承实例,并且这个类很简单而且特定于局部上下文时,匿名类非常有用。 12345678910111213141516171819202122interface PersonWriter{ public function write(Person $person);}class Person{ public $name = 'wu'; public function getName(PersonWriter $writer) { echo $writer->write($this), PHP_EOL; }}$person = new Person();$person->getName(new class implements PersonWriter{ public function write(Person $person) { echo $person->name, PHP_EOL; }}); 匿名类不支持闭包,也就无法访问定义在匿名类外的属性,但是可以通过构造函数将参数传入。

2021-03-21
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(); 这个模式太简单了,就不多说了。

2021-03-21
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();