思考一个问题:假如公司让你评价各开发成员的代码质量,好坏如何区分?
跟你玩的好的最好还是跟你有过过节的最差呢?这都不好说。
要评判好坏,我们是不是需要有一种标准?
相信不少同学都听说过“高内聚,低耦合”,即类的内聚性是不是很高,耦合度是不是很低。这一原则可以作为我们评判软件设计的好坏。
通俗的说,我们就需要尽量让写出的程序易于维护,减少程序与程序之间的复杂性、耦合度。
在php中,为了解决此类问题,有很多优秀的设计模式,比如单例模式、工厂模式等。
提到设计模式,有些同学头就懵了。很多时候,你可能不经意间就已经完成了某模式的实现,尽管你头脑中并没有此类概念。这都无所谓。
但是,在yii2的核心源码实现上,却有很多优秀的设计模式的体现。比如我们接下来要介绍的控制反转。
所谓控制反转,英文 Inversion of Control,简称 IOC,字面上可以理解为对xx的控制进行了一个反转,换句话说就是对xx的控制的另一种实现,是一种思路,一种逻辑思想。这个很容易理解。
既然是一种思路,那总有一种解决方案可以实现吧。这就是我们接下来要介绍的依赖注入。
依赖注入,英文 Dependency Injection,简称 DI,是 IOC 的一种典型实现。
那什么是依赖注入呢?
**简单的说,依赖注入就是把对象A所依赖的其他对象B呀C呀的,以属性或者构造函数的方式传递到对象A的内部,而不是直接在对象A内实例化。**
其目的就是为了让对象A和其依赖的其他对象解耦,减少二者的依赖。即通过“注入”的方式去解决依赖问题。
听不懂?没关系,我们来看一个用户注册成功后发送邮件的例子,撇开依赖注入,通常我们可以这样实现
~~~
class EmailSenderByQq
{
public function send()
{
}
}
class User
{
public function register()
{
// other code
$email = new EmailSenderByQq;
$email->send();
}
}
~~~
调用User类的register方法注册成功后,实例化邮件类 EmailSenderByQq 并调用邮件方法发送邮件。
从程序上我们可以看出,User类依赖邮件对象 EmailSenderByQq,没有 EmailSenderByQq 就发不了邮件。这一切看起来都很正常。
突然有一天,QQ 的邮箱服务器故障了,怎么办?
我们需要更换邮件类,比如我们要把所有依赖 EmailSenderByQq 对象的代码全部更新为163的邮件对象 EmailSenderBy163。
但是,我们的项目很庞大,很多地方都实例化了 EmailSenderByQq ,一个一个找到这个类,然后再一个一个的改掉,自己写的代码哭着也要说没事,万一是其他同事写的,心中瞬间有一万只@#*%**& 飞过...,项目的可维护性就不言而喻了。
有好的解决方法吗?
想一下我们刚刚讲过的控制反转,如果我们对二者进行解耦,会不会好一些?
我们把User类依赖的EmailSenderByQq对象,以构造函数的参数传递进去,降低User类对EmailSenderByQq对象的依赖
~~~
class User
{
private $_emailSenderObject;
public function __construct($emailSenderObject)
{
$this->_emailSenderObject = $emailSenderObject;
}
public function register()
{
// other code
$this->_emailSenderObject->send();
}
}
$emailSenderObject = new EmailSenderByQq;
$user = new User($emailSenderObject);
$user->register();
~~~
以属性的方式同样也可以实现,代码参考如下
~~~
class EmailSenderBy163
{
public function send()
{
}
}
class User
{
public $emailSenderClass;
public function register()
{
// other code
$this->emailSenderClass->send();
}
}
$user = new User;
$user->emailSenderClass = new EmailSenderBy163;
$user->register();
~~~
第一种通过构造参数传递对象和第二种通过属性传递对象的过程,其实就是依赖注入的体现。
“注入”,就是把一个实例传递到另一个实例的内部。
明白了以上,我们再继续对上面的代码优化一下
通过 EmailSenderByQq 类和EmailSenderBy163类,我们提炼一个 interface 接口,让User类的register方法依赖interface接口的对象看起来更合适。
以构造函数“注入”实例为例,代码我们整理如下
~~~
interface EmailSender
{
public function send();
}
class EmailSenderByQq implements EmailSender
{
public function send()
{
}
}
class EmailSenderBy163 implements EmailSender
{
public function send()
{
}
}
class User
{
public $emailSenderClass;
public function __construct(EmailSender $emailSenderObject)
{
$this->emailSenderClass = $emailSenderObject;
}
public function register()
{
// other code
$this->emailSenderClass->send();
}
}
$user = new User(new EmailSenderBy163);
$user->register();
~~~
如此,我们便实现了User类与EmailSender解耦的目的。
最后我们再来看看要讲解的依赖倒置原则。
依赖倒置原则(Dependence Inversion Principle, DIP),是一种软件设计思想。传统软件设计中,上层代码依赖于下层代码,当下层出现变动时, 上层代码也要相应变化,维护成本较高。而DIP的核心思想是上层定义接口,下层实现这个接口, 从而使得下层依赖于上层,降低耦合度,提高整个系统的弹性。这是一种经实践证明的有效策略。
回过神来我们发现,刚刚优化的代码是不是就是DIP原则的一种体现?
正如开篇所说,本文要讲解的内容不难,对吧?如果在学习的过程中有任何不理解的地方,下面尽管留言。
最后希望各位跟我一样,在成长的过程中,也学会慢慢优化代码,让你的代码更健壮一些,尽量不要再天马行空啦。
思考:尽管我们通过注入的方式解决了依赖问题,但是,假如A依赖B和C,B依赖D,C依赖E,D依赖F等等,那我们实例化A的时候,是不是更加复杂?
- 配置
- composer安装
- composer用法
- composer版本约束表达
- phpstorm
- sftp文件同步
- php类型约束
- laradock
- 配置文件缓存详解
- git
- 自定义函数
- 核心概念
- IOC
- 服务提供者
- Facade
- 契约
- 生命周期
- 路由
- 请求
- 命名路由
- 路由分组
- 资源路由
- 控制器路由
- 响应宏
- 响应
- Command
- 创建命令
- 定时任务
- console路由
- 执行用户自定义的定时任务
- artisan命令
- 中间件
- 创建中间件
- 使用中间件
- 前置和后置
- 详细介绍
- 访问次数限制
- 为 VerifyCsrfToken 添加过滤条件
- 单点登录
- 事件
- 创建
- ORM
- 简介
- DB类
- 配置
- CURD
- queryScope和setAttribute
- 查看sql执行过程
- 关联关系
- 一对一
- 一对多
- 多对多
- 远程关联
- 多态一对多
- 多态多对多
- 关联数据库的调用
- withDefault
- 跨模型更新时间戳
- withCount,withSum ,withAvg, withMax,withMin
- SQL常见操作
- 模型事件
- 模型事件详解
- 模型事件与 Observer
- deleted 事件未被触发
- model validation
- ORM/代码片段
- Repository模式
- 多重where语句
- 中间表类型转换
- Collection集合
- 新增的一些方法
- 常见用法
- 求和例子
- 机场登机例子
- 计算github活跃度
- 转化评论格式
- 计算营业额
- 创建lookup数组
- 重新组织出表和字段关系并且字段排序
- 重构循环
- 其他例子
- 其他问题一
- 去重
- 第二个数组按第一个数组的键值排序
- 搜索ES
- 安装
- 表单
- Request
- sessiom
- Response
- Input
- 表单验证
- 简介
- Validator
- Request类
- 接口中的表单验证
- Lumen 中自定义表单验证返回消息
- redis
- 广播事件
- 发布订阅
- 队列
- 守护进程
- redis队列的坑
- beanstalkd
- rabbitmq
- redis队列
- 日志模块
- 错误
- 日志详解
- 数据填充与迁移
- 生成数据
- 数据填充seed
- migrate
- 常见错误
- Blade模板
- 流程控制
- 子视图
- URL
- 代码片段
- Carbon时间类
- 一些用法
- 邮件
- 分页
- 加密解密
- 缓存
- 文件上传
- 优化
- 随记
- 嵌套评论
- 判断字符串是否是合法的 json 字符串
- 单元测试
- 计算出两个日期的diff
- 自定义一个类文件让composer加载
- 时间加减
- 对象数组互转方法
- 用户停留过久自动退出登录
- optional 辅助方法
- 文件下载
- Api
- Dingo api
- auth.basic
- api_token
- Jwt-Auth
- passport
- Auth
- Authentication 和 Authorization
- Auth Facade
- 授权策略
- Gates
- composer包
- debug包
- idehelp包
- image处理
- 验证码
- jq插件
- 第三方登录
- 第三方支付
- log显示包
- 微信包
- xss过滤
- Excel包
- MongoDB
- php操作
- 聚合查询
- 发送带附件邮件
- 中文转拼音包
- clockwork网页调试
- emoji表情
- symfony组件
- swooletw/laravel-swoole
- 常见问题
- 跨域问题
- Laravel队列优先级的一个坑
- cache:clear清除缓存问题
- .env无法读取
- 源码相关基础知识
- __set和__get
- 依赖注入、控制反转和依赖倒置原则
- 控制反转容器(Ioc Container)
- 深入服务容器
- call_user_func
- compact
- 中间件简易实现
- array_reduce
- 中间件实现代码
- Pipeline管道操作
- composer自动加载
- redis延时队列
- 了解laravel redis队列
- cli
- 源码解读
- Facade分析
- Facade源码分析
- IOC服务容器
- 中间件原理
- 依赖注入浅析
- 微信
- 微信公众号
- 常用接收消息
- 6大接收接口
- 常用被动回复消息
- 接口调用凭证
- 自定义菜单
- 新增素材
- 客服消息
- 二维码
- 微信语音
- LBS定位
- 网页授权
- JSSDK
- easywechat
- 小程序
- 小程序配置app.json