Deprecated
: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in
/www/wwwroot/testblog.58heshihu.com/var/Widget/Archive.php
on line
1057
首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
182 阅读
2
php接口优化 使用curl_multi_init批量请求
144 阅读
3
《从菜鸟到大师之路 ElasticSearch 篇》
107 阅读
4
2024年备考系统架构设计师
104 阅读
5
PHP 文件I/O
92 阅读
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
登录
Search
标签搜索
php函数
php语法
性能优化
安全
错误和异常处理
问题
vue
Composer
Session
缓存
框架
Swoole
api
并发
异步
正则表达式
php-fpm
mysql 索引
开发规范
协程
dafenqi
累计撰写
786
篇文章
累计收到
28
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
5
篇与
的结果
2023-08-11
php的依赖注入容器
php的依赖注入容器这里接着上一篇 php依赖注入,直接贴出完整代码如下:复制代码<?phpclass C{public function doSomething() { echo __METHOD__, '我是C类|'; }}class B{private $c; public function __construct(C $c) { $this->c = $c; } public function doSomething() { $this->c->doSomething(); echo __METHOD__, '我是B类|'; }}class A{private $b; public function __construct(B $b) { $this->b = $b; } public function doSomething() { $this->b->doSomething(); echo __METHOD__, '我是A类|';; }}//这段代码使用了魔术方法,在给不可访问属性赋值时,__set() 会被调用。读取不可访问属性的值时,__get() 会被调用。class Container{private $s = array(); function __set($k, $c) { $this->s[$k] = $c; } function __get($k) { return $this->s[$k]($this); }}$class = new Container();$class->c = function () {return new C();};$class->b = function ($class) {return new B($class->c);};$class->a = function ($class) {return new A($class->b);};// 从容器中取得A$model = $class->a;$model->doSomething(); // C::doSomething我是C类|B::doSomething我是B类|A::doSomething我是A类|复制代码再来一段简单的代码演示一下,容器代码来自simple di container,完整如下:复制代码<?phpclass C{public function doSomething() { echo __METHOD__, '我是C类|'; }}class B{private $c; public function __construct(C $c) { $this->c = $c; } public function doSomething() { $this->c->doSomething(); echo __METHOD__, '我是B类|'; }}class A{private $b; public function __construct(B $b) { $this->b = $b; } public function doSomething() { $this->b->doSomething(); echo __METHOD__, '我是A类|';; }}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('c', function () {return new C();});IoC::bind('b', function () {return new B(IoC::make('c'));});IoC::bind('a', function () {return new A(IoC::make('b'));});// 从容器中取得A$foo = IoC::make('a');$foo->doSomething(); // C::doSomething我是C类|B::doSomething我是B类|A::doSomething我是A类|复制代码这段代码使用了后期静态绑定依赖注入容器的高级功能真实的dependency injection container会提供更多的特性,如自动绑定(Autowiring)或 自动解析(Automatic Resolution)注释解析器(Annotations)延迟注入(Lazy injection)下面的代码在Twittee的基础上,实现了Autowiring。复制代码<?phpclass C{public function doSomething() { echo __METHOD__, '我是周伯通C|'; }}class B{private $c; public function __construct(C $c) { $this->c = $c; } public function doSomething() { $this->c->doSomething(); echo __METHOD__, '我是周伯通B|'; }}class A{private $b; public function __construct(B $b) { $this->b = $b; } public function doSomething() { $this->b->doSomething(); echo __METHOD__, '我是周伯通A|';; }}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.'); }}// ----$class = new Container();$class->b = 'B';$class->a = function ($class) {return new A($class->b);};// 从容器中取得A$model = $class->a;$model->doSomething(); $di = new Container();$di->php7 = 'A';/* @var A $php7 /$foo = $di->php7;var_dump($foo);$foo->doSomething(); //C::doSomething我是周伯通C|B::doSomething我是周伯通B|A::doSomething我是周伯通A|object(A)#10 (1) { ["b":"A":private]=> object(B)#14 (1) { ["c":"B":private]=> object(C)#16 (0) { } } } C::doSomething我是周伯通C|B::doSomething我是周伯通B|A::doSomething我是周伯通A|?>复制代码以上代码的原理参考PHP官方文档:反射,PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。若想进一步提供一个数组访问接口,如$di->php7可以写成$di'php7'],则需用到[ArrayAccess(数组式访问)接口 。一些复杂的容器会有许多特性,欢迎博友们补充。无论从事什么行业,只要做好两件事就够了,一个是你的专业、一个是你的人品,专业决定了你的存在,人品决定了你的人脉,剩下的就是坚持,用善良專業和真诚赢取更多的信任。
2023年08月11日
18 阅读
0 评论
0 点赞
2023-08-08
php中的数种依赖注入
php中的数种依赖注入经常看到却一直不甚理解的概念,依赖注入(DI)以及控制器反转(Ioc),找了几篇好的文章,分享一下。 自己理解的,依赖注入就是组件通过构造器,方法或者属性字段来获取相应的依赖对象。举个现实生活中的例子来理解, 比如我要一把菜刀 如何获得1.可以自己造一把,对应new一个。2.可以找生产菜刀的工厂去买一把,对应工厂模式。3.可以打电话 让店家送货上门,对应依赖注入。再比如我是一个演员,我不可能要求某个导演,我要演某某剧的男一号,相反,导演可以决定让谁来演。而我们的object就是这个演员。注入的几个途径:1.construct注入帮助<?phpclass Book { private $db_conn; public function __construct($db_conn) { $this->db_conn = $db_conn;}}但是如果依赖过多,那么在构造方法里必然传入多个参数,三个以上就会使代码变的难以阅读。2.set注入帮助<?php $book = new Book(); $book->setdb($db); $book->setprice($price); $book->set_author($author);?>代码很清晰,但是当我们需要注入第四个依赖时,意味着又要增加一行。比较好的解决办法是 建立一个class作为所有依赖关系的container,在这个class中可以存放、创建、获取、查找需要的依赖关系帮助<?phpclass Ioc { protected $db_conn; public static function make_book() { $new_book = new Book(); $new_book->set_db(self::$db_conn); //... //... //其他的依赖注入 return $new_book;}}此时,如果获取一个book实例,只需要执行$newone = Ioc::makebook();以上是container的一个具体实例,最好还是不要把具体的某个依赖注入写成方法,采用registry注册,get获取比较好。帮助<?phpclass Ioc {/**@var 注册的依赖数组*/ protected static $registry = array(); /**添加一个resolve到registry数组中@param string $name 依赖标识@param object $resolve 一个匿名函数用来创建实例@return void */public static function register($name, Closure $resolve) {static::$registry[$name] = $resolve;} /**返回一个实例@param string $name 依赖的标识@return mixed */public static function resolve($name) { if ( static::registered($name) ) { $name = static::$registry[$name]; return $name(); } throw new Exception('Nothing registered with that name, fool.');} /**查询某个依赖实例是否存在@param string $name id@return bool */public static function registered($name) {return array_key_exists($name, static::$registry);}}现在就可以通过如下方式来注册和注入一个依赖帮助<?php$book = Ioc::registry('book', function(){$book = new Book;$book->setdb('...');$book->setprice('...');return $book;});//注入依赖$book = Ioc::resolve('book');?>http://net.tutsplus.com/tutorials/php/dependency-injection-huhhttp://scriptogr.am/mattsah/post/dependencies-in-phphttp://martinfowler.com/articles/injection.htmlhttp://www.potstuck.com/2009/01/08/php-dependency-injection/http://www.potstuck.com/2010/09/09/php-dependency-a-php-dependency-injection-framework/http://www.cnblogs.com/Seekr/archive/2012/06/20/2556463.html
2023年08月08日
10 阅读
0 评论
0 点赞
2023-08-08
话说 依赖注入(DI) or 控制反转(IoC)
话说 依赖注入(DI) or 控制反转(IoC)科普:首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程序间的耦合,鄙人学习了一下,看TP官网还没有相关的文章,就写下这篇拙作介绍一下这种设计模式,希望能为TP社区贡献一些力量。首先先别追究这个设计模式的定义,否则你一定会被说的云里雾里,笔者就是深受其害,百度了N多文章,都是从理论角度来描述,充斥着大量的生涩词汇,要么就是java代码描述的,也生涩。不管怎么样,总算弄清楚一些了,下面就以php的角度来描述一下依赖注入这个概念。先假设我们这里有一个类,类里面需要用到数据库连接,按照最最原始的办法,我们可能是这样写这个类的:class example {private $_db; function __construct(){ include "./Lib/Db.php"; $this->_db = new Db("localhost","root","123456","test"); } function getList(){ $this->_db->query("......");//这里具体sql语句就省略不写了 }}复制代码过程:在构造函数里先将数据库类文件include进来;然后又通过new Db并传入数据库连接信息实例化db类;之后getList方法就可以通过$this->_db来调用数据库类,实现数据库操作。看上去我们实现了想要的功能,但是这是一个噩梦的开始,以后example1,example2,example3....越来越多的类需要用到db组件,如果都这么写的话,万一有一天数据库密码改了或者db类发生变化了,岂不是要回头修改所有类文件?ok,为了解决这个问题,工厂模式出现了,我们创建了一个Factory方法,并通过Factory::getDb()方法来获得db组件的实例:class Factory {public static function getDb(){ include "./Lib/Db.php"; return new Db("localhost","root","123456","test"); }}复制代码sample类变成:class example {private $_db; function __construct(){ $this->_db = Factory::getDb(); } function getList(){ $this->_db->query("......");//这里具体sql语句就省略不写了 }}复制代码这样就完美了吗?再次想想一下以后example1,example2,example3....所有的类,你都需要在构造函数里通过Factory::getDb();获的一个Db实例,实际上你由原来的直接与Db类的耦合变为了和Factory工厂类的耦合,工厂类只是帮你把数据库连接信息给包装起来了,虽然当数据库信息发生变化时只要修改Factory::getDb()方法就可以了,但是突然有一天工厂方法需要改名,或者getDb方法需要改名,你又怎么办?当然这种需求其实还是很操蛋的,但有时候确实存在这种情况,一种解决方式是:我们不从example类内部实例化Db组件,我们依靠从外部的注入,什么意思呢?看下面的例子:class example {private $_db; function getList(){ $this->_db->query("......");//这里具体sql语句就省略不写了 } //从外部注入db连接 function setDb($connection){ $this->_db = $connection; }} //调用$example = new example();$example->setDb(Factory::getDb());//注入db连接$example->getList();复制代码这样一来,example类完全与外部类解除耦合了,你可以看到Db类里面已经没有工厂方法或Db类的身影了。我们通过从外部调用example类的setDb方法,将连接实例直接注入进去。这样example完全不用关心db连接怎么生成的了。这就叫依赖注入,实现不是在代码内部创建依赖关系,而是让其作为一个参数传递,这使得我们的程序更容易维护,降低程序代码的耦合度,实现一种松耦合。这还没完,我们再假设example类里面除了db还要用到其他外部类,我们通过:$example->setDb(Factory::getDb());//注入db连接$example->setFile(Factory::getFile());//注入文件处理类$example->setImage(Factory::getImage());//注入Image处理类 ...复制代码我们没完没了的写这么多set?累不累?ok,为了不用每次写这么多行代码,我们又去弄了一个工厂方法:class Factory {public static function getExample(){ $example = new example(); $example->setDb(Factory::getDb());//注入db连接 $example->setFile(Factory::getFile());//注入文件处理类 $example->setImage(Factory::getImage());//注入Image处理类 return $expample; }}复制代码实例化example时变为:$example=Factory::getExample();$example->getList();复制代码似乎完美了,但是怎么感觉又回到了上面第一次用工厂方法时的场景?这确实不是一个好的解决方案,所以又提出了一个概念:容器,又叫做IoC容器、DI容器。我们本来是通过setXXX方法注入各种类,代码很长,方法很多,虽然可以通过一个工厂方法包装,但是还不是那么爽,好吧,我们不用setXXX方法了,这样也就不用工厂方法二次包装了,那么我们还怎么实现依赖注入呢?这里我们引入一个约定:在example类的构造函数里传入一个名为Di $di的参数,如下:class example {private $_di; function __construct(Di &$di){ $this->_di = $di; } //通过di容器获取db实例 function getList(){ $this->_di->get('db')->query("......");//这里具体sql语句就省略不写了 }}$di = new Di();$di->set("db",function(){ return new Db("localhost","root","root","test"); });$example = new example($di);$example->getList();复制代码Di就是IoC容器,所谓容器就是存放我们可能会用到的各种类的实例,我们通过$di->set()设置一个名为db的实例,因为是通过回调函数的方式传入的,所以set的时候并不会立即实例化db类,而是当$di->get('db')的时候才会实例化,同样,在设计di类的时候还可以融入单例模式。这样我们只要在全局范围内申明一个Di类,将所有需要注入的类放到容器里,然后将容器作为构造函数的参数传入到example,即可在example类里面从容器中获取实例。当然也不一定是构造函数,你也可以用一个 setDi(Di $di)的方法来传入Di容器,总之约定是你制定的,你自己清楚就行。这样一来依赖注入以及关键的容器概念已经介绍完毕,剩下的就是在实际中使用并理解它吧!本文如有错误,请务必指出,大家共同学习,共同进步。楼主讲完了 80% 还有剩下的 20% 精华部分没有说到。就像您说的 在 example 里面放入 Factory工厂对象 是一种依赖,不好。同样的。您在 example 里面放入 DI 对象依赖 也是不好的。那我们应该怎么做呢?答案就是: 所有涉及到实例化对象的地方,都不要用 new class() 去实例化了。我们用一个 叫 DI 的容器对象 去实例化 所有对象。假设我们现在需要 用ID容器 去实例化 example 对象。class example {private $_db; function __construct(db){ $this->_db = db } function getList(){ $this->_db->query("......");//这里具体sql语句就省略不写了 }}复制代码复制代码DI 对象有一个方法 ,用途是 分析类的构造函数的参数。这里我们分析 example 的构造函数, 发现有一个叫 db 的参数。DI 就会去实例化 db对象。 然后 把实例化的 db对象 传入 example 的构造函数。经过这个步骤。example 对象就创建出来了, 而且自动传入了 db对象。过程简单描述如下:1 将 db 对象注册到 DI 容器。2 DI对象分析 example 类。 发现需要一个叫 db 的对象3 DI查找 db 对象, 发现找到了, 实例化 db 对象。4 调用 example 对构造函数,传入 db。 这样 example 就实例化完成了。$example = DI->invoke('example')复制代码复制代码大概就是这样个过程吧。不知道说的对不对。 呵呵。
2023年08月08日
11 阅读
0 评论
0 点赞
2023-08-08
PHP程序员如何理解IoC/DI
PHP程序员如何理解IoC/DI思想思想是解决问题的根本思想必须转换成习惯构建一套完整的思想体系是开发能力成熟的标志——《简单之美》(前言).“成功的软件项目就是那些提交产物达到或超出客户的预期的项目,而且开发过程符合时间和费用上的要求,结果在面对变化和调整时有弹性。”——《面向对象分析与设计》(第3版)P.236术语介绍——引用《Spring 2.0 技术手册》林信良非侵入性 No intrusive框架的目标之一是非侵入性(No intrusive)组件可以直接拿到另一个应用或框架之中使用增加组件的可重用性(Reusability)容器(Container)管理对象的生成、资源取得、销毁等生命周期建立对象与对象之间的依赖关系启动容器后,所有对象直接取用,不用编写任何一行代码来产生对象,或是建立对象之间的依赖关系。IoC控制反转 Inversion of Control依赖关系的转移依赖抽象而非实践DI依赖注入 Dependency Injection不必自己在代码中维护对象的依赖容器自动根据配置,将依赖注入指定对象AOPAspect-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();这个组件现在可以很简单的获取到它所需要的服务,服务采用延迟加载的方式,只有在需要使用的时候才初始化,这也节省了服务器资源。这个组件现在是高度解耦。例如,我们可以替换掉创建连接的方式,它们的行为或它们的任何其他方面,也不会影响该组件。参考文章http://docs.phalconphp.com/zh/latest/reference/di.htmlWhat is Dependency Injection? Fabien PotencierInversion of Control Containers and the Dependency Injection pattern by Martin Fowler补充很多代码背后,都是某种哲学思想的体现。以下引用《面向模式的软件架构》卷1模式系统第六章模式与软件架构软件架构支持技术(开发软件时要遵循的基本原则)抽象封装信息隐藏分离关注点耦合与内聚充分、完整、简单策略与实现分离策略组件负责上下文相关决策,解读信息的语义和含义,将众多不同结果合并或选择参数值实现组件负责执行定义完整的算法,不需要作出与上下文相关的决策。上下文和解释是外部的,通常由传递给组件的参数提供。接口与实现分离接口部分定义了组件提供的功能以及如何使用该组件。组件的客户端可以访问该接口。实现部分包含实现组件提供的功能的实际代码,还可能包含仅供组件内部使用的函数和数据结构。组件的客户端不能访问其实现部分。单个引用点软件系统中的任何元素都应只声明和定义一次,避免不一致性问题。分而治之软件架构的非功能特性可修改性可维护性可扩展性重组可移植性互操作性与其它系统或环境交互效率可靠性容错:发生错误时确保行为正确并自行修复健壮性:对应用程序进行保护,抵御错误的使用方式和无效输入,确保发生意外错误时处于指定状态。可测试性可重用性通过重用开发软件开发软件时考虑重用
2023年08月08日
19 阅读
0 评论
0 点赞
2023-08-08
PHP程序员如何理解依赖注入容器(dependency injection container)
PHP程序员如何理解依赖注入容器(dependency injection container)背景知识传统的思路是应用程序用到一个Foo类,就会创建Foo类并调用Foo类的方法,假如这个方法内需要一个Bar类,就会创建Bar类并调用Bar类的方法,而这个方法内需要一个Bim类,就会创建Bim类,接着做些其它工作。<?php// 代码【1】class Bim{public function doSomething() { echo __METHOD__, '|'; }}class Bar{public function doSomething() { $bim = new Bim(); $bim->doSomething(); echo __METHOD__, '|'; }}class Foo{public function doSomething() { $bar = new Bar(); $bar->doSomething(); echo __METHOD__; }}$foo = new Foo();$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething使用依赖注入的思路是应用程序用到Foo类,Foo类需要Bar类,Bar类需要Bim类,那么先创建Bim类,再创建Bar类并把Bim注入,再创建Foo类,并把Bar类注入,再调用Foo方法,Foo调用Bar方法,接着做些其它工作。<?php// 代码【2】class Bim{public function doSomething() { echo __METHOD__, '|'; }}class Bar{private $bim; public function __construct(Bim $bim) { $this->bim = $bim; } public function doSomething() { $this->bim->doSomething(); echo __METHOD__, '|'; }}class Foo{private $bar; public function __construct(Bar $bar) { $this->bar = $bar; } public function doSomething() { $this->bar->doSomething(); echo __METHOD__; }}$foo = new Foo(new Bar(new Bim()));$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething这就是控制反转模式。依赖关系的控制反转到调用链的起点。这样你可以完全控制依赖关系,通过调整不同的注入对象,来控制程序的行为。例如Foo类用到了memcache,可以在不修改Foo类代码的情况下,改用redis。使用依赖注入容器后的思路是应用程序需要到Foo类,就从容器内取得Foo类,容器创建Bim类,再创建Bar类并把Bim注入,再创建Foo类,并把Bar注入,应用程序调用Foo方法,Foo调用Bar方法,接着做些其它工作.总之容器负责实例化,注入依赖,处理依赖关系等工作。代码演示 依赖注入容器 (dependency injection container)通过一个最简单的容器类来解释一下,这段代码来自 Twittee<?phpclass Container{private $s = array(); function __set($k, $c) { $this->s[$k] = $c; } function __get($k) { return $this->s[$k]($this); }}这段代码使用了魔术方法,在给不可访问属性赋值时,__set() 会被调用。读取不可访问属性的值时,__get() 会被调用。<?php$c = new Container();$c->bim = function () {return new Bim();};$c->bar = function ($c) {return new Bar($c->bim);};$c->foo = function ($c) {return new Foo($c->bar);};// 从容器中取得Foo$foo = $c->foo;$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething这段代码使用了匿名函数再来一段简单的代码演示一下,容器代码来自simple di container<?phpclass 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('bim', function () {return new Bim();});IoC::bind('bar', function () {return new Bar(IoC::make('bim'));});IoC::bind('foo', function () {return new Foo(IoC::make('bar'));});// 从容器中取得Foo$foo = IoC::make('foo');$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething这段代码使用了后期静态绑定依赖注入容器 (dependency injection container) 高级功能真实的dependency injection container会提供更多的特性,如自动绑定(Autowiring)或 自动解析(Automatic Resolution)注释解析器(Annotations)延迟注入(Lazy injection)下面的代码在Twittee的基础上,实现了Autowiring。<?phpclass Bim{public function doSomething() { echo __METHOD__, '|'; }}class Bar{private $bim; public function __construct(Bim $bim) { $this->bim = $bim; } public function doSomething() { $this->bim->doSomething(); echo __METHOD__, '|'; }}class Foo{private $bar; public function __construct(Bar $bar) { $this->bar = $bar; } public function doSomething() { $this->bar->doSomething(); echo __METHOD__; }}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->bar = 'Bar';$c->foo = function ($c) {return new Foo($c->bar);};// 从容器中取得Foo$foo = $c->foo;$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething// ----$di = new Container();$di->foo = 'Foo';/* @var Foo $foo /$foo = $di->foo;var_dump($foo);/*Foo#10 (1) { private $bar => class Bar#14 (1) {private $bim => class Bim#16 (0) { }}}*/$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething以上代码的原理参考PHP官方文档:反射,PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。若想进一步提供一个数组访问接口,如$di->foo可以写成$di['foo'],则需用到ArrayAccess(数组式访问)接口 。一些复杂的容器会有许多特性,下面列出一些相关的github项目,欢迎补充。参考代码Twitteesimple di containerPimplePHP-DIDing推荐阅读PHP程序员如何理解IoC/DIPHP之道PHP最佳实践
2023年08月08日
13 阅读
0 评论
0 点赞