首页
归档
朋友
关于我
留言
Search
1
虚拟机无法ping不通百度,并无法访问浏览器
4,847 阅读
2
mysql使用or条件使索引失效
4,061 阅读
3
mysql如何在一对多查询时选取时间最近的一条记录
3,475 阅读
4
根据MySQL获取当天,昨天,本周,本月,上周,上月,本月的起始时间
2,927 阅读
5
熟悉mysql的共享锁、排它锁、悲观锁、乐观锁以及使用场景
1,766 阅读
PHP
面向对象
设计模式
知识汇总
常用函数
PHP框架知识
数据库
MySQL
服务器
Docker
虚拟机
Nginx
缓存相关
Redis
前端
中间件
RabbitMQ
网络编程
HTTP相关
Swoole
Workerman
工具软件
Git
Typecho
杂乱无章
面试指南
PHP相关
MySQL面试汇总
中间件相关
开发技巧 | 优化
登录
Search
标签搜索
php
mysql
代码片段
linux
Thinkphp
Redis
nginx
mysql优化
docker
面试指南
面向对象
git
Laravel框架
http协议
RabbitMQ
Redis性能优化
设计模式
linux命令
编译安装
PhpSpreadsheet
黎明强
累计撰写
70
篇文章
累计收到
59
条评论
首页
栏目
PHP
面向对象
设计模式
知识汇总
常用函数
PHP框架知识
数据库
MySQL
服务器
Docker
虚拟机
Nginx
缓存相关
Redis
前端
中间件
RabbitMQ
网络编程
HTTP相关
Swoole
Workerman
工具软件
Git
Typecho
杂乱无章
面试指南
PHP相关
MySQL面试汇总
中间件相关
开发技巧 | 优化
页面
归档
朋友
关于我
留言
搜索到
2
篇与
设计模式
的结果
2022-11-08
项目代码if else太多,使用策略+工厂模式来解决
简述策略模式,又称为政策模式,属于行为型的设计模式。定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而变化 。业务场景假设有这样的业务场景,拉取不同部门的数据,根据不同的角色类型获取不同的Json内容数据,多数小伙伴会写出如下代码:if(type=="1"){ //按照1格式解析 }else if(type=="2"){ //按2格式解析 }else{ //按照默认格式解析 } 这个代码可能会存在哪些 问题呢 ?如果分支变多,这里的代码就会变得 臃肿,难以维护,可读性低 。如果你需要接入一种新的解析类型,那只能在 原有代码上修改 。说得专业一点的话,就是以上代码,违背了面向对象编程的开闭原则以及 单一原则 。开闭原则 (对于扩展是开放的,但是对于修改是封闭的):增加或者删除某个逻辑,都需要修改到原来代码单一原则 (规定一个类应该只有一个发生变化的原因):修改任何类型的分支逻辑代码,都需要改动当前类的代码。如果你的代码就是:有多个if...else等条件分支,并且每个条件分支,可以封装起来替换的,我们就可以使用策略模式来优化。策略模式使用一个接口或者抽象类,里面两个方法(一个方法匹配类型,一个可替换的逻辑实现方法)不同策略的差异化实现(就是说,不同策略的实现类)使用策略模式一个接口,两个方法不同策略的差异化实现示例封装接口类、策略实现类<?php //统一实现接口 interface Strategy { function send(); } //实现 class ServiceFollowLog implements Strategy { public function send() { // TODO: Implement get() method. echo "记录网络客服的日志"; } } class SaleFollowLog implements Strategy{ public function send() { // TODO: Implement get() method. echo "记录业务员日志"; } } // 工厂类 class Factory{ public $products=array(); public function get($type){ return $this->products[$type]; } public function register($type){ $class=ucfirst($type); $this->products[$type]=new $class; } } 应用层(接口):class user { // public $types=array("sms","email"); public $types= ["3"=> "ServiceFollowLog" , "4"=> "SaleFollowLog"]; public $factory=null; public function __construct(){ //先生成出所有策略的对象 $this->factory=new Factory(); foreach(array_values($this->types) as $t){ $this->factory->register($t); } } //-----新的代码示例:改造策略模式----------- public function newDoAction(){ //根据传递的type参数 , 选择使用哪一个策略 $post="3"; $notice=$this->factory->get($this->types[$post]); $notice->send(); } //---------旧的代码示例------------ public function OldDoAction(){ $type = "sms"; if($type == "sms"){ echo "sms"; }elseif ($type =="email"){ echo "email"; }elseif ($type == "qq"){ echo "qq"; } } } 调用输出// 输出 $u = new user(); $u->newDoAction();每个类都是一个策略,如果有新的策略,增只需要添加一个策略类并把类注册进来。保证了每个类的职责单一性
2022年11月08日
159 阅读
0 评论
0 点赞
2020-11-07
理解依赖注入(DI)和控制反转(IOC) 、容器
简介依赖注入 Dependency Injection 简称 DI,目的是让代码耦合度降低,模块化程度高,让代码更易测试什么是依赖为什么会有依赖?因为我们为了模块化,把各种小功能都做成了一个模块,模块之间相互调用,这样就产生了依赖。耦合一个好的代码结构设计一定是松耦合的,这也是很多通用设计模式的宗旨,就是把分散在各处的同一个功能的代码汇聚到一起,形成一个模块,然后在不同模块之间通过一些细小的、明确的渠道进行沟通。在实践中,不同功能和模块之间的互相依赖是不可避免的,而如何处理好这些依赖之间的关系则是代码结构能否变得美好的关键。没有用依赖注入的情况传统的思路是应用程序用到一个User 类,就会创建User 类并调用User 类的方法,假如这个方法内需要一个Notify 类,就会创建Notify 类并调用Notify 类的方法,而这个方法内需要一个Email 类,就会创建Email 类,接着做些其它工作。//用户类 class User{ public function register($user) { echo "用户注册 | "; // 注册操作 // 发送确认邮件 $notify = new Notify(); $notify->sendEmail('register', $user); } } //通知类 class Notify{ public function sendEmail($type, $data) { echo "发送通知 | "; switch ($type) { case 'register': // 发送注册确认邮件 $email = new Email($type); $email->send($data); } } } //邮箱类 class Email{ public function send($data) { echo "发送邮件 | "; // 发送邮件 } } $user = new User(); $user->register('用户:黎明强'); // 用户注册 | 发送通知 | 发送邮件 |上述代码中,三个类之间逐层依赖,三个类实例化的顺序是 User -> Notify -> Email 。也就是说我先实例化User类,可能执行了一些代码之后再去实例化我需要的其他类,比如Notify,以此类推。这种依赖会让我们不得不为了得到需要的依赖而去做的一些准备工作,有时候可能一个new操作还不够。而这部分工作就是所说的耦合,他会让一个独立功能的类不得不去关心一些和自己的主体功能没什么关系的操作。如何解除一个类对其他类的依赖?要解决这个问题也很简单,我可以先实例化好Email类,然后再实例化Notify,然后把Email对象作为参数传给Notify,最后实例化User类,然后把Notify传进去。这就是所谓的依赖注入,可以看到这个过程中类实例化的顺序完全反过来了,先实例化被依赖的对象,而不是先实例化最终需要的对象,这是控制反转。使用依赖注入可以通过构造函数来注入需要的依赖,也可以用一些其他的方法。代码如下//用户类 class User{ protected $notify; public function __construct(Notify $notify) { $this->notify = $notify; } public function register($user) { echo "用户注册 | "; $this->notify->sendEmail('register',$user); } } //通知类 class Notify{ protected $email ; public function __construct(Email $email) { $this->email = $email; } public function sendEmail($type, $data) { echo "发送通知 | "; switch ($type) { case 'register': // 发送注册确认邮件 $this->email->send($data); } } } //邮箱类 class Email{ public function send($data) { echo '发送邮件 | '; // 发送邮件 } } //调用------- $email = new Email(); $notify = new Notify($email); $user = new User($notify); $user->register('liming'); // 用户注册 | 发送通知 | 发送邮件 |使用依赖注入的好处是显而易见的,我们通过参数了,让 email对象通过参数传到了 noitfy 类中,而不是在 noitfy 类中的方法中 实例化 email 类,从而将 email类和 noitfy 类解耦 。使用依赖注入容器来管理依赖那又有新的问题,例子中只有三个类还好,那如果这个User类依赖Notify来发邮件,依赖Model来存数据库,依赖redis来缓存,这样固然把依赖关系转移到了类的外部,但还是会导致我只想实例化一下User的时候,却要手动做很多的准备工作,会让代码混乱。所以这个时候需要一个容器。而这个容器的作用就是替我来管理这些依赖。1 、 定义容器类 这段代码使用了魔术方法,在给不可访问属性赋值时,__set() 会被调用。读取不可访问属性的值时,__get() 会被调用。// 容器 class Container { private $s = array(); function __set($k, $c) { $this->s[$k] = $c; } function __get($k) { return $this->s[$k]($this); } }在程序启动的时候,我们可以在一个地方统一的注册好一系列的基础服务。$c = new Container(); $c->email = function () { return new Email(); }; $c->notify = function ($c) { return new Notify($c->email); }; $c->user = function ($c) { return new User($c->notify); }; // 从容器中取得user $foo = $c->user; $foo->register('liming'); // 用户注册 | 发送通知 | 发送邮件 | 这段代码使用了匿名函数总之容器负责实例化,注入依赖,处理依赖关系等工作。再来一段简单的代码演示一下,容器代码class IoC { protected static $registry = []; public static function bind($name, Callable $resolver) { static::$registry[$name] = $resolver; } public static function make($name) { if (isset(static::$registry[$name])) { $resolver = static::$registry[$name]; return $resolver(); } throw new Exception('Alias does not exist in the IoC registry.'); } } IoC::bind('email', function () { return new Email(); }); IoC::bind('noitfy', function () { return new Notify(IoC::make('email')); }); IoC::bind('user', function () { return new User(IoC::make('noitfy')); }); echo "<pre>"; // 从容器中取得User $foo = IoC::make('user'); $foo->register('liming'); // 用户注册 | 发送通知 | 发送邮件 | 这段代码使用了后期静态绑定依赖注入容器 (dependency injection container) 高级功能 (难点)自动绑定(Autowiring)或 自动解析(Automatic Resolution)注释解析器(Annotations)延迟注入(Lazy injection)//用户类 class User{ protected $notify; public function __construct(Notify $notify) { $this->notify = $notify; } public function register($user) { echo "用户注册 | "; $this->notify->sendEmail('register',$user); } } //通知类 class Notify{ protected $email ; public function __construct(Email $email) { $this->email = $email; } public function sendEmail($type, $data) { echo "发送通知 | "; switch ($type) { case 'register': // 发送注册确认邮件 $this->email->send($data); } } } //邮箱类 class Email{ public function send($data) { echo '发送邮件 | '; // 发送邮件 } } class Container { private $s = array(); public function __set($k, $c) { $this->s[$k] = $c; } public function __get($k) { // return $this->s[$k]($this); return $this->build($this->s[$k]); } /** * 自动绑定(Autowiring)自动解析(Automatic Resolution) * * @param string $className * @return object * @throws Exception */ public function build($className) { // 如果是匿名函数(Anonymous functions),也叫闭包函数(closures) if ($className instanceof Closure) { // 执行闭包函数,并将结果 return $className($this); } /** @var ReflectionClass $reflector */ $reflector = new ReflectionClass($className); // 检查类是否可实例化, 排除抽象类abstract和对象接口interface if (!$reflector->isInstantiable()) { throw new Exception("Can't instantiate this."); } /** @var ReflectionMethod $constructor 获取类的构造函数 */ $constructor = $reflector->getConstructor(); // 若无构造函数,直接实例化并返回 if (is_null($constructor)) { return new $className; } // 取构造函数参数,通过 ReflectionParameter 数组返回参数列表 $parameters = $constructor->getParameters(); // 递归解析构造函数的参数 $dependencies = $this->getDependencies($parameters); // 创建一个类的新实例,给出的参数将传递到类的构造函数。 return $reflector->newInstanceArgs($dependencies); } /** * @param array $parameters * @return array * @throws Exception */ public function getDependencies($parameters) { $dependencies = []; /** @var ReflectionParameter $parameter */ foreach ($parameters as $parameter) { /** @var ReflectionClass $dependency */ $dependency = $parameter->getClass(); if (is_null($dependency)) { // 是变量,有默认值则设置默认值 $dependencies[] = $this->resolveNonClass($parameter); } else { // 是一个类,递归解析 $dependencies[] = $this->build($dependency->name); } } return $dependencies; } /** * @param ReflectionParameter $parameter * @return mixed * @throws Exception */ public function resolveNonClass($parameter) { // 有默认值则返回默认值 if ($parameter->isDefaultValueAvailable()) { return $parameter->getDefaultValue(); } throw new Exception('I have no idea what to do here.'); } } // ---- $c = new Container(); $c->noitfy = 'Notify'; $c->user = function ($c) { return new User($c->noitfy); }; // 从容器中取得Foo $foo = $c->user; $foo->register('liming'); // 用户注册 | 发送通知 | 发送邮件 | // ---- $di = new Container(); $di->user = 'User'; /** @var Foo $user */ $user = $di->user; echo "<pre>"; print_r($user); /* User Object ( [notify:protected] => Notify Object ( [email:protected] => Email Object ( ) ) ) */ $user->register('liming'); // 用户注册 | 发送通知 | 发送邮件 |以上代码的原理参考PHP官方文档:反射,PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。若想进一步提供一个数组访问接口,如di->user可以写成di−>user可以写成di['user'],则需用到ArrayAccess(数组式访问)接口 。一些复杂的容器会有许多特性,下面列出一些相关的github项目,欢迎补充。参考代码Twitteesimple di containerPimplePHP-DIDing推荐阅读PHP程序员如何理解IoC/DIPHP之道PHP最佳实践
2020年11月07日
966 阅读
0 评论
0 点赞