php 和设计模式 - 注册模式
发表于|更新于|设计模式
|总字数:163|阅读时长:1分钟|浏览量:
注册模式通常将对象注册到一个全局的对象树上,需要的时候从对象树上取下需要的实例,就像卖糖葫芦的有木有。不过不一样的是糖葫芦会摘完,对象树摘下后并不会销毁该对象。
注册模式通常通过一个只有静态方法的抽象类来存放这些对象。或者通过单例模式。
1 | abstract class registry |
文章作者: m-finder
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 M-finder!
相关推荐

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();

2021-03-21
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()); 好了,生成器模式到此结束。

2021-03-21
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]); 还算简单,就是依次往下传递。

2021-03-21
php 和设计模式 - 工厂模式
工厂模式工厂模式是一种类,它具有为你创建对象的某些方法,你可以通过工厂创建对象,而不是直接 new,这样当你需要替换创建的对象类型时,只需要修改工厂即可。 根据抽象程度不同,工厂模式又分为简单工厂、工厂方法和抽象工厂三种。 简单工厂简单工厂与静态工厂的唯一区别是有没有使用静态方法生成实例,因此这里不再将其分为两种模式。 123456789101112131415161718192021222324252627282930313233343536373839404142interface Car{ public function makeCar();}class BMWCar implements Car{ public function makeCar() { echo '来一辆别摸我', PHP_EOL; }}class VolvoCar implements Car{ public function makeCar() { echo '来一辆沃尔沃', PHP_EOL; }}class Factory{ public static function createBMW(): BMWCar { return new BMWCar(); } public static function createVolvo(): VolvoCar { return new VolvoCar(); }}$bmw = Factory::createBMW();$bmw->makeCar();$volvo = Factory::createVolvo();$volvo->makeCar(); 实现比较简单,但是当我们要新增一种车🚗时,就必须修改工厂,这在一定程度上违反了开闭原则。所以严格意义上简单工厂不属于 23 种设计模式。 工厂方法工厂方法是针对每一种产品提供一个工厂类,通过不同的工厂实例来创建不同的产品实例。解决了简单工厂新增具体实现需要修改工厂的问题,支持增加任意同一种抽象产品,不修改原有工厂。 那么,把刚才的简单工厂改造一下吧: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253interface Car{ public function makeCar();}class BMWCar implements Car{ public function makeCar() { echo '来一辆别摸我', PHP_EOL; }}class VolvoCar implements Car{ public function makeCar() { echo '来一辆沃尔沃', PHP_EOL; }}interface Factory{ public static function getInstance(): Car;}class BMwFactory implements Factory{ /** * @return BMWCar */ public static function getInstance(): Car { return new BMWCar(); }}class VolvoFactory implements Factory{ public static function getInstance(): Car { return new VolvoCar(); }}$bmw = BMwFactory::getInstance();$bmw->makeCar();$volvo = VolvoFactory::getInstance();$volvo->makeCar(); 不同的工厂生产不同的产品,新增产品类型时,只需要新建一个工厂即可,不再需要改动原有工厂,符合了开闭原则。 抽象工厂抽象工厂与工厂方法的区别是,抽象工厂用于生产一系列产品,工厂方法只生产一个产品,会产生大量的工厂类。 宝马生产跑车、轿车还有 mini,宝马是一个产品族,跑车、轿车等是产品的等级。那么,也就是说一个工厂,可以生产相同品牌的不同产品。 那就不写🌰了吧。懒。 总结简单工厂增加产品不方便。工厂方法增加产品很方便,但是会产生大量的工厂类。抽象工厂可以生产多个产品,但是新增产品也需要修改工厂。

2021-03-21
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(); 比较适用于树形菜单、文件和文件夹管理等,感觉场景很有限啊……

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; }}); 匿名类不支持闭包,也就无法访问定义在匿名类外的属性,但是可以通过构造函数将参数传入。