思想

思想是解决问题的根本

思想必须转换成习惯

构建一套完整的思想体系是开发能力成熟的标志

——《简单之美》(前言)

.

“成功的软件项目就是那些提交产物达到或超出客户的预期的项目,而且开发过程符合时间和费用上的要求,结果在面对变化和调整时有弹性。”

——《面向对象分析与设计》(第3版)P.236

术语介绍

——引用《Spring 2.0 技术手册》林信良

非侵入性 No intrusive

框架的目标之一是非侵入性(No intrusive)

组件可以直接拿到另一个应用或框架之中使用

增加组件的可重用性(Reusability)

容器(Container)

管理对象的生成、资源取得、销毁等生命周期

建立对象与对象之间的依赖关系

启动容器后,所有对象直接取用,不用编写任何一行代码来产生对象,或是建立对象之间的依赖关系。

IoC

控制反转 Inversion of Control

依赖关系的转移

依赖抽象而非实践

DI

依赖注入 Dependency Injection

不必自己在代码中维护对象的依赖

容器自动根据配置,将依赖注入指定对象

AOP

Aspect-oriented programming

面向方面编程

无需修改任何一行程序代码,将功能加入至原先的应用程序中,也可以在不修改任何程序的情况下移除。

分层

表现层:提供服务,显示信息。

领域层:逻辑,系统中真正的核心。

数据源层:与数据库、消息系统、事务管理器及其它软件包通信。

——《企业应用架构模式》P.14

代码演示IoC

假设应用程序有储存需求,若直接在高层的应用程序中调用低层模块API,导致应用程序对低层模块产生依赖。

/**

* 高层

*/

class Business

{

private $writer;

public function __construct()

{

$this->writer = new FloppyWriter();

}

public function save()

{

$this->writer->saveToFloppy();

}

}

/**

* 低层,软盘存储

*/

class FloppyWriter

{

public function saveToFloppy()

{

echo __METHOD__;

}

}

$biz = new Business();

$biz->save(); // FloppyWriter::saveToFloppy

假设程序要移植到另一个平台,而该平台使用USB磁盘作为存储介质,则这个程序无法直接重用,必须加以修改才行。本例由于低层变化导致高层也跟着变化,不好的设计。

正如前方提到的

控制反转 Inversion of Control

依赖关系的转移

依赖抽象而非实践

程序不应该依赖于具体的实现,而是要依赖抽像的接口。请看代码演示

/**

* 接口

*/

interface IDeviceWriter

{

public function saveToDevice();

}

/**

* 高层

*/

class Business

{

/**

* @var IDeviceWriter

*/

private $writer;

/**

* @param IDeviceWriter $writer

*/

public function setWriter($writer)

{

$this->writer = $writer;

}

public function save()

{

$this->writer->saveToDevice();

}

}

/**

* 低层,软盘存储

*/

class FloppyWriter implements IDeviceWriter

{

public function saveToDevice()

{

echo __METHOD__;

}

}

/**

* 低层,USB盘存储

*/

class UsbDiskWriter implements IDeviceWriter

{

public function saveToDevice()

{

echo __METHOD__;

}

}

$biz = new Business();

$biz->setWriter(new UsbDiskWriter());

$biz->save(); // UsbDiskWriter::saveToDevice

$biz->setWriter(new FloppyWriter());

$biz->save(); // FloppyWriter::saveToDevice

控制权从实际的FloppyWriter转移到了抽象的IDeviceWriter接口上,让Business依赖于IDeviceWriter接口,且FloppyWriter、UsbDiskWriter也依赖于IDeviceWriter接口。

这就是IoC,面对变化,高层不用修改一行代码,不再依赖低层,而是依赖注入,这就引出了DI。

比较实用的注入方式有三种:

Setter injection 使用setter方法

Constructor injection 使用构造函数

Property Injection 直接设置属性

事实上不管有多少种方法,都是IoC思想的实现而已,上面的代码演示的是Setter方式的注入。

依赖注入容器 Dependency Injection Container

管理应用程序中的『全局』对象(包括实例化、处理依赖关系)。

可以延时加载对象(仅用到时才创建对象)。

促进编写可重用、可测试和松耦合的代码。

理解了IoC和DI之后,就引发了另一个问题,引用Phalcon文档描述如下:

如果这个组件有很多依赖, 我们需要创建多个参数的setter方法​​来传递依赖关系,或者建立一个多个参数的构造函数来传递它们,另外在使用组件前还要每次都创建依赖,这让我们的代码像这样不易维护

//创建依赖实例或从注册表中查找

$connection = new Connection();

$session = new Session();

$fileSystem = new FileSystem();

$filter = new Filter();

$selector = new Selector();

//把实例作为参数传递给构造函数

$some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector);

// ... 或者使用setter

$some->setConnection($connection);

$some->setSession($session);

$some->setFileSystem($fileSystem);

$some->setFilter($filter);

$some->setSelector($selector);

假设我们必须在应用的不同地方使用和创建这些对象。如果当你永远不需要任何依赖实例时,你需要去删掉构造函数的参数,或者去删掉注入的setter。为了解决这样的问题,我们再次回到全局注册表创建组件。不管怎么样,在创建对象之前,它增加了一个新的抽象层:

class SomeComponent

{

// ...

/**

* Define a factory method to create SomeComponent instances injecting its dependencies

*/

public static function factory()

{

$connection = new Connection();

$session = new Session();

$fileSystem = new FileSystem();

$filter = new Filter();

$selector = new Selector();

return new self($connection, $session, $fileSystem, $filter, $selector);

}

}

瞬间,我们又回到刚刚开始的问题了,我们再次创建依赖实例在组件内部!我们可以继续前进,找出一个每次能奏效的方法去解决这个问题。但似乎一次又一次,我们又回到了不实用的例子中。

一个实用和优雅的解决方法,是为依赖实例提供一个容器。这个容器担任全局的注册表,就像我们刚才看到的那样。使用依赖实例的容器作为一个桥梁来获取依赖实例,使我们能够降低我们的组件的复杂性:

class SomeComponent

{

protected $_di;

public function __construct($di)

{

$this->_di = $di;

}

public function someDbTask()

{

// 获得数据库连接实例

// 总是返回一个新的连接

$connection = $this->_di->get('db');

}

public function someOtherDbTask()

{

// 获得共享连接实例

// 每次请求都返回相同的连接实例

$connection = $this->_di->getShared('db');

// 这个方法也需要一个输入过滤的依赖服务

$filter = $this->_di->get('filter');

}

}

$di = new Phalcon\DI();

//在容器中注册一个db服务

$di->set('db', function() {

return new Connection(array(

"host" => "localhost",

"username" => "root",

"password" => "secret",

"dbname" => "invo"

));

});

//在容器中注册一个filter服务

$di->set('filter', function() {

return new Filter();

});

//在容器中注册一个session服务

$di->set('session', function() {

return new Session();

});

//把传递服务的容器作为唯一参数传递给组件

$some = new SomeComponent($di);

$some->someTask();

这个组件现在可以很简单的获取到它所需要的服务,服务采用延迟加载的方式,只有在需要使用的时候才初始化,这也节省了服务器资源。这个组件现在是高度解耦。例如,我们可以替换掉创建连接的方式,它们的行为或它们的任何其他方面,也不会影响该组件。

参考文章

补充

很多代码背后,都是某种哲学思想的体现。

以下引用《面向模式的软件架构》卷1模式系统第六章模式与软件架构

软件架构支持技术(开发软件时要遵循的基本原则)

抽象

封装

信息隐藏

分离关注点

耦合与内聚

充分、完整、简单

策略与实现分离

策略组件负责上下文相关决策,解读信息的语义和含义,将众多不同结果合并或选择参数值

实现组件负责执行定义完整的算法,不需要作出与上下文相关的决策。上下文和解释是外部的,通常由传递给组件的参数提供。

接口与实现分离

接口部分定义了组件提供的功能以及如何使用该组件。组件的客户端可以访问该接口。

实现部分包含实现组件提供的功能的实际代码,还可能包含仅供组件内部使用的函数和数据结构。组件的客户端不能访问其实现部分。

单个引用点

软件系统中的任何元素都应只声明和定义一次,避免不一致性问题。

10. 分而治之

软件架构的非功能特性

可修改性

可维护性

可扩展性

重组

可移植性

互操作性

与其它系统或环境交互

效率

可靠性

容错:发生错误时确保行为正确并自行修复

健壮性:对应用程序进行保护,抵御错误的使用方式和无效输入,确保发生意外错误时处于指定状态。

可测试性

可重用性

通过重用开发软件

开发软件时考虑重用

ioc di php,PHP程序员如何理解IoC/DI相关推荐

  1. 程序员如何理解客户需求

    作为A8U扎金花技术人员,精力都投入到技术上,那么我们,程序员,热爱计算机技术.当你的客户或老板,大声的脱口说出他们想要的东西时,我们无法阻止,我们只能想象如何去实现它们. 但是,我们需要用更全面的眼 ...

  2. 浅谈对程序员的认识_8年编程生涯回顾:谈谈我对程序员的理解

    点击右上方关注我们,每天都能收到这样有趣有料的推文啦! (全文2000字,建议阅读时长5分钟) [文章为语录体问答形式] hello,大家好,我是阿琰. 今天这个文章主要是想讲一下我作为程序员对这个职 ...

  3. 程序员才能理解的20张动图,你懂几个?

    图自:网络 下面这些动图你能理解几个? ▼▼▼ 「0」只改了一行代码-- 「1」产品经理第10次改需求后, 告诉我还是用第一版 「2」赶工三个月的项目, 第一次启动整体测试时 「3」当程序员打开了遗留 ...

  4. 程序员为什么不自己写程序去卖?只有老程序员才理解的道理

    我以前就这么干过,干不下去了,因为个人写不了大型程序,只能写小程序,小程序也很难接单,接了单也很容易被客户坑跑单.而且接的单会越来越触犯规定,搞不好就进去了. 写程序只是一项技能,和人力资源,财务,销 ...

  5. .NET程序员应该理解的几种软件保护方法 辛苦开发的程序需要建立有效的保护机制...

    使用.NET开发程序,因为元数据存在于程序集中,可以轻易的被反编译成源代码.在分发给客户之间,会应用加密软件混淆程序集,这样让程序集被反编译时, 理解起来困难一些,增加一点破解难度.以下列举我常见到的 ...

  6. php与tcp哪个快,PHP程序员如何理解TCP协议

    理解成一个TCP连接就是两根相反流动的水管,水就是其中的数据, 数据是没有边界的,水流也没有界限,因此你需要根据水流的大小,来截取对应的数据,解码成你需要的数据. 比如协议规定tcp 的头部2个字节表 ...

  7. base.dispose(disposing) 未将对象引用到实例_程序员深入理解asp.net c#值类型和引用类型...

    基本概念 CLR支持两种类型:值类型和引用类型. 面试过很多5年左右的同学,有很多连值类型和引用类型的基本概念都回答不上来,难道现在的c#开发人员基础这么弱了吗?还是大家都不重视基础呢?这个随便找一篇 ...

  8. 程序员如何理解Gmail“撤销发送”功能

     飞鸽传书用GMAIL已经10年了.可能是史上最长内测之后,谷歌终于推出 Gmail"撤销发送"功能.这项功能之前已经测试了长达 6 年时间,现在变成了 Gmail 一个永久性 ...

  9. 如何理解IoC/DI

    思想 思想是解决问题的根本 思想必须转换成习惯 构建一套完整的思想体系是开发能力成熟的标志 --<简单之美>(前言) . "成功的软件项目就是那些提交产物达到或超出客户的预期的项 ...

最新文章

  1. Linux系统性能分析:内存 优化
  2. 【恋爱通告】高清完整版迅雷下载! 首发
  3. C# 按钮控制windows音量
  4. Vue项目登录成功后返回到原操作页面
  5. react学习笔记(4)组件的生命周期(运行阶段和销毁阶段)以及事件处理函数
  6. Microsoft SQL Server学习(二)
  7. 参加开发竞赛遇到的问题【总结】
  8. 中枪!这才是当代博士生真实日常大赏
  9. 自己配置python环境_windows下python环境的配置
  10. 将GPIO外设挂到Cortex_M3 AHB总线上详细流程扩展外设步骤总结
  11. java 斗地主 案例
  12. HTML网页制作代码大全——中华传统文化设计题材网站(html+css)
  13. 计算机二级模拟系统在线,计算机二级foxbase题库系统
  14. 【Java】——命名规范
  15. Metasploit联动CobaltStrike渗透win11主机并提权
  16. 上班族一定得学会、掌握的jpg转word技巧
  17. 【语音控制SU-03T的使用】
  18. error C2448: 'Unknown' : function-style initializer appears to be a function definition
  19. 陆奇,59岁,创业者:真正的高手,都是时间的长期主义者!
  20. Microsoft Visual Studio + Qt插件编程出现错误error MSB4184问题

热门文章

  1. OpenCV图像的轮廓
  2. 计算机微课比赛获奖作品,我院学生的微课作品在“中国大学生计算机设计大赛”西北赛区决赛中荣获一等奖...
  3. 查找和平精英的数据包在android哪,和平精英公测FAQ 和平精英新手问题解答_游侠手游...
  4. macos 读取ntfs
  5. Laravel Facade的加载过程及原理
  6. 【ppt课件制作】Focusky教程 | 自制模板
  7. 如何给图片批量命名?其实方法很简单
  8. java内嵌chrome浏览器,JS和JAVA如何进行交互
  9. plc维修入门与故障实例pdf_实例讲解之西门子伺服电机维修超温报警故障
  10. 网站变成灰色——grayscale