PHP 依赖注入,自此不再考虑加载顺序
PHP 依赖注入,从此不再考虑加载顺序
说这个话题之前先讲一个比较高端的思想--'依赖倒置原则'
"依赖倒置是一种软件设计思想,在传统软件中,上层代码依赖于下层代码,当下层代码有所改动时,上层代码也要相应进行改动,因此维护成本较高。而依赖倒置原则的思想是,上层不应该依赖下层,应依赖接口。意为上层代码定义接口,下层代码实现该接口,从而使得下层依赖于上层接口,降低耦合度,提高系统弹性"
上面的解释有点虚,下面我们以实际代码来解释这个理论
比如有这么条需求,用户注册完成后要发送一封邮件,然后你有如下代码:
先有邮件类'Email.class.php'
<span style="color: #0000ff;">class</span> <span style="color: #008080;">Mail</span><span style="color: #000000;">{ </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> send() { </span><span style="color: #008000;">/*</span><span style="color: #008000;">这里是如何发送邮件的代码</span><span style="color: #008000;">*/</span><span style="color: #000000;"> }}</span>
然后又注册的类'Register.class.php'
<span style="color: #0000ff;">class</span><span style="color: #000000;"> Register{ </span><span style="color: #0000ff;">private</span> <span style="color: #800080;">$_emailObj</span><span style="color: #000000;">; </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> doRegister() { </span><span style="color: #008000;">/*</span><span style="color: #008000;">这里是如何注册</span><span style="color: #008000;">*/</span> <span style="color: #800080;">$this</span>->_emailObj = <span style="color: #0000ff;">new</span> <span style="color: #008080;">Mail</span><span style="color: #000000;">(); </span><span style="color: #800080;">$this</span>->_emailObj->send();<span style="color: #008000;">//</span><span style="color: #008000;">发送邮件</span><span style="color: #000000;"> }}</span>
然后开始注册
<span style="color: #0000ff;">include</span> 'Mail.class.php'<span style="color: #000000;">;</span><span style="color: #0000ff;">include</span> 'Register.class.php'<span style="color: #000000;">;</span><span style="color: #800080;">$reg</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Register();</span><span style="color: #800080;">$reg</span>->doRegister();
看起来事情很简单,你很快把这个功能上线了,看起来相安无事... xxx天过后,产品人员说发送邮件的不好,要使用发送短信的,然后你说这简单我把'Mail'类改下...
又过了几天,产品人员说发送短信费用太高,还是改用邮件的好... 此时心中一万个草泥马奔腾而过...
这种事情,常常在产品狗身上发生,无可奈何花落去...
以上场景的问题在于,你每次不得不对'Mail'类进行修改,代码复用性很低,高层过度依赖于底层。那么我们就考虑'依赖倒置原则',让底层继承高层制定的接口,高层依赖于接口。
<span style="color: #0000ff;">interface</span> <span style="color: #008080;">Mail</span><span style="color: #000000;">{ </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> send();}</span>
<span style="color: #0000ff;">class</span> Email <span style="color: #0000ff;">implements</span> <span style="color: #008080;">Mail</span><span style="color: #000000;">(){ </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> send() { </span><span style="color: #008000;">//</span><span style="color: #008000;">发送Email</span><span style="color: #000000;"> }}</span>
<span style="color: #0000ff;">class</span> SmsMail <span style="color: #0000ff;">implements</span> <span style="color: #008080;">Mail</span><span style="color: #000000;">(){ </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> send() { </span><span style="color: #008000;">//</span><span style="color: #008000;">发送短信</span><span style="color: #000000;"> }}</span>
<span style="color: #0000ff;">class</span><span style="color: #000000;"> Register{ </span><span style="color: #0000ff;">private</span> <span style="color: #800080;">$_mailObj</span><span style="color: #000000;">; </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(<span style="color: #008080;">Mail</span> <span style="color: #800080;">$mailObj</span><span style="color: #000000;">) { </span><span style="color: #800080;">$this</span>->_mailObj = <span style="color: #800080;">$mailObj</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> doRegister() { </span><span style="color: #008000;">/*</span><span style="color: #008000;">这里是如何注册</span><span style="color: #008000;">*/</span> <span style="color: #800080;">$this</span>->_mailObj->send();<span style="color: #008000;">//</span><span style="color: #008000;">发送信息</span><span style="color: #000000;"> }}</span>
下面开始发送信息
<span style="color: #008000;">/*</span><span style="color: #008000;"> 此处省略若干行 </span><span style="color: #008000;">*/</span><span style="color: #800080;">$reg</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Register();</span><span style="color: #800080;">$emailObj</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Email();</span><span style="color: #800080;">$smsObj</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> SmsMail();</span><span style="color: #800080;">$reg</span>->doRegister(<span style="color: #800080;">$emailObj</span>);<span style="color: #008000;">//</span><span style="color: #008000;">使用email发送</span><span style="color: #800080;">$reg</span>->doRegister(<span style="color: #800080;">$smsObj</span>);<span style="color: #008000;">//</span><span style="color: #008000;">使用短信发送</span><span style="color: #008000;">/*</span><span style="color: #008000;"> 你甚至可以发完邮件再发短信 </span><span style="color: #008000;">*/</span>
上面的代码解决了'Register'对信息发送类的依赖,使用构造函数注入的方法,使得它只依赖于发送短信的接口,只要实现其接口中的'send'方法,不管你怎么发送都可以。上例就使用了"注入"这个思想,就像注射器一样将一个类的实例注入到另一个类的实例中去,需要用什么就注入什么。当然"依赖倒置原则"也始终贯彻在里面。"注入"不仅可以通过构造函数注入,也可以通过属性注入,上面你可以可以通过一个"setter"来动态为"mailObj"这个属性赋值。
上面看了很多,但是有心的读者可能会发现标题中"从此不再考虑加载顺序"这个字眼,你上面的不还是要考虑加载顺序吗? 不还是先得引入信息发送类,然后在引入注册类,然后再实例化吗? 如果类一多,不照样晕!
确实如此,现实中有许多这样的案例,一开始类就那么多,慢慢的功能越来越多,人员越来越多,编写了很多类,要使用这个类必须先引入那个类,而且一定要确保顺序正确。有这么个例子, "a 依赖于b, b 依赖于c, c 依赖于 d, d 依赖于e", 要获取'a'的实例,你必须依次引入 'e,d,c,b'然后依次进行实例化,老的员工知道这个坑,跳过去了。某天来了个新人,他想实例化'a' 可是一直报错,他都不造咋回事,此时只能看看看'a'的业务逻辑,然后知道要先获取'b'的实例,然后在看'b'的业务逻辑,然后... 一天过去了,他还是没有获取到'a'的实例,然后领导来了...
那这个事情到底是新人的技术低下,还是当时架构人员的水平低下了?
现在切入话题,来实现如何不考虑加载顺序,在实现前就要明白要是不考虑加载顺序就意味着让程序自动进行加载自动进行实例化。类要实例化,只要保证完整的传递给'__construct'函数所必须的参数就OK了,在类中如果要引用其他类,也必须在构造函数中注入,否则调用时仍然会发生错误。那么我们需要一个类,来保存类实例化所需要的参数,依赖的其他类或者对象以及各个类实例化后的引用
该类命名为盒子 'Container.class.php', 其内容如下:
<span style="color: #008000;">/*</span><span style="color: #008000;">** 依赖注入类</span><span style="color: #008000;">*/</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Container{ </span><span style="color: #008000;">/*</span><span style="color: #008000;">* [email protected] array 存储各个类的定义 以类的名称为键 </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">private</span> <span style="color: #800080;">$_definitions</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">(); </span><span style="color: #008000;">/*</span><span style="color: #008000;">* [email protected] array 存储各个类实例化需要的参数 以类的名称为键 </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">private</span> <span style="color: #800080;">$_params</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">(); </span><span style="color: #008000;">/*</span><span style="color: #008000;">* [email protected] array 存储各个类实例化的引用 </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">private</span> <span style="color: #800080;">$_reflections</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">(); </span><span style="color: #008000;">/*</span><span style="color: #008000;">* * @var array 各个类依赖的类 </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">private</span> <span style="color: #800080;">$_dependencies</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">(); </span><span style="color: #008000;">/*</span><span style="color: #008000;">* * 设置依赖 * @param string $class 类、方法 名称 * @param mixed $defination 类、方法的定义 * @param array $params 类、方法初始化需要的参数 </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> set(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$defination</span> = <span style="color: #0000ff;">array</span>(), <span style="color: #800080;">$params</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">()) { </span><span style="color: #800080;">$this</span>->_params[<span style="color: #800080;">$class</span>] = <span style="color: #800080;">$params</span><span style="color: #000000;">; </span><span style="color: #800080;">$this</span>->_definitions[<span style="color: #800080;">$class</span>] = <span style="color: #800080;">$this</span>->initDefinition(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$defination</span><span style="color: #000000;">); } </span><span style="color: #008000;">/*</span><span style="color: #008000;">* * 获取实例 * @param string $class 类、方法 名称 * @param array $params 实例化需要的参数 * @param array $properties 为实例配置的属性 * @return mixed </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> get(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span> = <span style="color: #0000ff;">array</span>(), <span style="color: #800080;">$properties</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">()) { </span><span style="color: #0000ff;">if</span>(!<span style="color: #0000ff;">isset</span>(<span style="color: #800080;">$this</span>->_definitions[<span style="color: #800080;">$class</span><span style="color: #000000;">])) {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果重来没有声明过 则直接创建</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span>->bulid(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span>, <span style="color: #800080;">$properties</span><span style="color: #000000;">); } </span><span style="color: #800080;">$defination</span> = <span style="color: #800080;">$this</span>->_definitions[<span style="color: #800080;">$class</span><span style="color: #000000;">]; </span><span style="color: #0000ff;">if</span>(<span style="color: #008080;">is_callable</span>(<span style="color: #800080;">$defination</span>, <span style="color: #0000ff;">true</span><span style="color: #000000;">)) {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果声明是函数</span> <span style="color: #800080;">$params</span> = <span style="color: #800080;">$this</span>->parseDependencies(<span style="color: #800080;">$this</span>->mergeParams(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span><span style="color: #000000;">)); </span><span style="color: #800080;">$obj</span> = <span style="color: #008080;">call_user_func</span>(<span style="color: #800080;">$defination</span>, <span style="color: #800080;">$this</span>, <span style="color: #800080;">$params</span>, <span style="color: #800080;">$properties</span><span style="color: #000000;">); } </span><span style="color: #0000ff;">elseif</span>(<span style="color: #008080;">is_array</span>(<span style="color: #800080;">$defination</span><span style="color: #000000;">)) { </span><span style="color: #800080;">$originalClass</span> = <span style="color: #800080;">$defination</span>['class'<span style="color: #000000;">]; </span><span style="color: #0000ff;">unset</span>(<span style="color: #800080;">$definition</span>['class'<span style="color: #000000;">]); </span><span style="color: #008000;">//</span><span style="color: #008000;">difinition中除了'class'元素外 其他的都当做实例的属性处理</span> <span style="color: #800080;">$properties</span> = <span style="color: #008080;">array_merge</span>((<span style="color: #0000ff;">array</span>)<span style="color: #800080;">$definition</span>, <span style="color: #800080;">$properties</span><span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;">合并该类、函数声明时的参数</span> <span style="color: #800080;">$params</span> = <span style="color: #800080;">$this</span>->mergeParams(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span><span style="color: #000000;">); </span><span style="color: #0000ff;">if</span>(<span style="color: #800080;">$originalClass</span> === <span style="color: #800080;">$class</span><span style="color: #000000;">) {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果声明中的class的名称和关键字的名称相同 则直接生成对象</span> <span style="color: #800080;">$obj</span> = <span style="color: #800080;">$this</span>->bulid(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span>, <span style="color: #800080;">$properties</span><span style="color: #000000;">); } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果不同则有可能为别名 则从容器中获取</span> <span style="color: #800080;">$obj</span> = <span style="color: #800080;">$this</span>->get(<span style="color: #800080;">$originalClass</span>, <span style="color: #800080;">$params</span>, <span style="color: #800080;">$properties</span><span style="color: #000000;">); } } </span><span style="color: #0000ff;">elseif</span>(<span style="color: #008080;">is_object</span>(<span style="color: #800080;">$defination</span><span style="color: #000000;">)) {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果是个对象 直接返回</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">$defination</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> { </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">Exception</span>(<span style="color: #800080;">$class</span> . ' 声明错误!'<span style="color: #000000;">); } </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$obj</span><span style="color: #000000;">; } </span><span style="color: #008000;">/*</span><span style="color: #008000;">* * 合并参数 * @param string $class 类、函数 名称 * @param array $params 参数 * @return array </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">function</span> mergeParams(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">()) { </span><span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">empty</span>(<span style="color: #800080;">$this</span>->_params[<span style="color: #800080;">$class</span><span style="color: #000000;">])) { </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$params</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">empty</span>(<span style="color: #800080;">$params</span><span style="color: #000000;">)) { </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span>-><span style="color: #000000;">_params; } </span><span style="color: #800080;">$result</span> = <span style="color: #800080;">$this</span>->_params[<span style="color: #800080;">$class</span><span style="color: #000000;">]; </span><span style="color: #0000ff;">foreach</span>(<span style="color: #800080;">$params</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$key</span> => <span style="color: #800080;">$value</span><span style="color: #000000;">) { </span><span style="color: #800080;">$result</span>[<span style="color: #800080;">$key</span>] = <span style="color: #800080;">$value</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$result</span><span style="color: #000000;">; } </span><span style="color: #008000;">/*</span><span style="color: #008000;">* * 初始化声明 * @param string $class 类、函数 名称 * @param array $defination 类、函数的定义 * @return mixed </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">function</span> initDefinition(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$defination</span><span style="color: #000000;">) { </span><span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">empty</span>(<span style="color: #800080;">$defination</span><span style="color: #000000;">)) { </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">array</span>('class' => <span style="color: #800080;">$class</span><span style="color: #000000;">); } </span><span style="color: #0000ff;">if</span>(<span style="color: #008080;">is_string</span>(<span style="color: #800080;">$defination</span><span style="color: #000000;">)) { </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">array</span>('class' => <span style="color: #800080;">$defination</span><span style="color: #000000;">); } </span><span style="color: #0000ff;">if</span>(<span style="color: #008080;">is_callable</span>(<span style="color: #800080;">$defination</span>) || <span style="color: #008080;">is_object</span>(<span style="color: #800080;">$defination</span><span style="color: #000000;">)) { </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$defination</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">if</span>(<span style="color: #008080;">is_array</span>(<span style="color: #800080;">$defination</span><span style="color: #000000;">)) { </span><span style="color: #0000ff;">if</span>(!<span style="color: #0000ff;">isset</span>(<span style="color: #800080;">$defination</span>['class'<span style="color: #000000;">])) { </span><span style="color: #800080;">$definition</span>['class'] = <span style="color: #800080;">$class</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$defination</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">Exception</span>(<span style="color: #800080;">$class</span>. ' 声明错误'<span style="color: #000000;">); } </span><span style="color: #008000;">/*</span><span style="color: #008000;">* * 创建类实例、函数 * @param string $class 类、函数 名称 * @param array $params 初始化时的参数 * @param array $properties 属性 * @return mixed </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">function</span> bulid(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span>, <span style="color: #800080;">$properties</span><span style="color: #000000;">) { </span><span style="color: #0000ff;">list</span>(<span style="color: #800080;">$reflection</span>, <span style="color: #800080;">$dependencies</span>) = <span style="color: #800080;">$this</span>->getDependencies(<span style="color: #800080;">$class</span><span style="color: #000000;">); </span><span style="color: #0000ff;">foreach</span> ((<span style="color: #0000ff;">array</span>)<span style="color: #800080;">$params</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$index</span> => <span style="color: #800080;">$param</span><span style="color: #000000;">) {</span><span style="color: #008000;">//</span><span style="color: #008000;">依赖不仅有对象的依赖 还有普通参数的依赖</span> <span style="color: #800080;">$dependencies</span>[<span style="color: #800080;">$index</span>] = <span style="color: #800080;">$param</span><span style="color: #000000;">; } </span><span style="color: #800080;">$dependencies</span> = <span style="color: #800080;">$this</span>->parseDependencies(<span style="color: #800080;">$dependencies</span>, <span style="color: #800080;">$reflection</span><span style="color: #000000;">); </span><span style="color: #800080;">$obj</span> = <span style="color: #800080;">$reflection</span>->newInstanceArgs(<span style="color: #800080;">$dependencies</span><span style="color: #000000;">); </span><span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">empty</span>(<span style="color: #800080;">$properties</span><span style="color: #000000;">)) { </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$obj</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">foreach</span> ((<span style="color: #0000ff;">array</span>)<span style="color: #800080;">$properties</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$name</span> => <span style="color: #800080;">$value</span><span style="color: #000000;">) { </span><span style="color: #800080;">$obj</span>-><span style="color: #800080;">$name</span> = <span style="color: #800080;">$value</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$obj</span><span style="color: #000000;">; } </span><span style="color: #008000;">/*</span><span style="color: #008000;">* * 获取依赖 * @param string $class 类、函数 名称 * @return array </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">function</span> getDependencies(<span style="color: #800080;">$class</span><span style="color: #000000;">) { </span><span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">isset</span>(<span style="color: #800080;">$this</span>->_reflections[<span style="color: #800080;">$class</span><span style="color: #000000;">])) {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果已经实例化过 直接从缓存中获取</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">array</span>(<span style="color: #800080;">$this</span>->_reflections[<span style="color: #800080;">$class</span>], <span style="color: #800080;">$this</span>->_dependencies[<span style="color: #800080;">$class</span><span style="color: #000000;">]); } </span><span style="color: #800080;">$dependencies</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">(); </span><span style="color: #800080;">$ref</span> = <span style="color: #0000ff;">new</span> ReflectionClass(<span style="color: #800080;">$class</span>);<span style="color: #008000;">//</span><span style="color: #008000;">获取对象的实例</span> <span style="color: #800080;">$constructor</span> = <span style="color: #800080;">$ref</span>->getConstructor();<span style="color: #008000;">//</span><span style="color: #008000;">获取对象的构造方法</span> <span style="color: #0000ff;">if</span>(<span style="color: #800080;">$constructor</span> !== <span style="color: #0000ff;">null</span><span style="color: #000000;">) {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果构造方法有参数</span> <span style="color: #0000ff;">foreach</span>(<span style="color: #800080;">$constructor</span>->getParameters() <span style="color: #0000ff;">as</span> <span style="color: #800080;">$param</span><span style="color: #000000;">) {</span><span style="color: #008000;">//</span><span style="color: #008000;">获取构造方法的参数</span> <span style="color: #0000ff;">if</span>(<span style="color: #800080;">$param</span>-><span style="color: #000000;">isDefaultValueAvailable()) {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果是默认 直接取默认值</span> <span style="color: #800080;">$dependencies</span>[] = <span style="color: #800080;">$param</span>-><span style="color: #000000;">getDefaultValue(); } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {</span><span style="color: #008000;">//</span><span style="color: #008000;">将构造函数中的参数实例化</span> <span style="color: #800080;">$temp</span> = <span style="color: #800080;">$param</span>-><span style="color: #000000;">getClass(); </span><span style="color: #800080;">$temp</span> = (<span style="color: #800080;">$temp</span> === <span style="color: #0000ff;">null</span> ? <span style="color: #0000ff;">null</span> : <span style="color: #800080;">$temp</span>-><span style="color: #000000;">getName()); </span><span style="color: #800080;">$temp</span> = Instance::getInstance(<span style="color: #800080;">$temp</span>);<span style="color: #008000;">//</span><span style="color: #008000;">这里使用Instance 类标示需要实例化 并且存储类的名字</span> <span style="color: #800080;">$dependencies</span>[] = <span style="color: #800080;">$temp</span><span style="color: #000000;">; } } } </span><span style="color: #800080;">$this</span>->_reflections[<span style="color: #800080;">$class</span>] = <span style="color: #800080;">$ref</span><span style="color: #000000;">; </span><span style="color: #800080;">$this</span>->_dependencies[<span style="color: #800080;">$class</span>] = <span style="color: #800080;">$dependencies</span><span style="color: #000000;">; </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">array</span>(<span style="color: #800080;">$ref</span>, <span style="color: #800080;">$dependencies</span><span style="color: #000000;">); } </span><span style="color: #008000;">/*</span><span style="color: #008000;">* * 解析依赖 * @param array $dependencies 依赖数组 * @param array $reflection 实例 * @return array $dependencies </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">function</span> parseDependencies(<span style="color: #800080;">$dependencies</span>, <span style="color: #800080;">$reflection</span> = <span style="color: #0000ff;">null</span><span style="color: #000000;">) { </span><span style="color: #0000ff;">foreach</span> ((<span style="color: #0000ff;">array</span>)<span style="color: #800080;">$dependencies</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$index</span> => <span style="color: #800080;">$dependency</span><span style="color: #000000;">) { </span><span style="color: #0000ff;">if</span>(<span style="color: #800080;">$dependency</span><span style="color: #000000;"> instanceof Instance) { </span><span style="color: #0000ff;">if</span> (<span style="color: #800080;">$dependency</span>->id !== <span style="color: #0000ff;">null</span><span style="color: #000000;">) { </span><span style="color: #800080;">$dependencies</span>[<span style="color: #800080;">$index</span>] = <span style="color: #800080;">$this</span>->get(<span style="color: #800080;">$dependency</span>-><span style="color: #000000;">id); } </span><span style="color: #0000ff;">elseif</span>(<span style="color: #800080;">$reflection</span> !== <span style="color: #0000ff;">null</span><span style="color: #000000;">) { </span><span style="color: #800080;">$parameters</span> = <span style="color: #800080;">$reflection</span>->getConstructor()-><span style="color: #000000;">getParameters(); </span><span style="color: #800080;">$name</span> = <span style="color: #800080;">$parameters</span>[<span style="color: #800080;">$index</span>]-><span style="color: #000000;">getName(); </span><span style="color: #800080;">$class</span> = <span style="color: #800080;">$reflection</span>-><span style="color: #000000;">getName(); </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">Exception</span>('实例化类 ' . <span style="color: #800080;">$class</span> . ' 时缺少必要参数:' . <span style="color: #800080;">$name</span><span style="color: #000000;">); } } } </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$dependencies</span><span style="color: #000000;">; }}</span>
下面是'Instance'类的内容,该类主要用于记录类的名称,标示是否需要获取实例
<span style="color: #0000ff;">class</span><span style="color: #000000;"> Instance{ </span><span style="color: #008000;">/*</span><span style="color: #008000;">* * @var 类唯一标示 </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">public</span> <span style="color: #800080;">$id</span><span style="color: #000000;">; </span><span style="color: #008000;">/*</span><span style="color: #008000;">* * 构造函数 * @param string $id 类唯一ID * @return void </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(<span style="color: #800080;">$id</span><span style="color: #000000;">) { </span><span style="color: #800080;">$this</span>->id = <span style="color: #800080;">$id</span><span style="color: #000000;">; } </span><span style="color: #008000;">/*</span><span style="color: #008000;">* * 获取类的实例 * @param string $id 类唯一ID * @return Object Instance </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">function</span> getInstance(<span style="color: #800080;">$id</span><span style="color: #000000;">) { </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span> self(<span style="color: #800080;">$id</span><span style="color: #000000;">); }}</span>
然后我们在'Container.class.php'中还是实现了为类的实例动态添加属性的功能,若要动态添加属性,需使用魔术方法'__set'来实现,因此所有使用依赖加载的类需要实现该方法,那么我们先定义一个基础类 'Base.class.php',内容如下
<span style="color: #0000ff;">class</span><span style="color: #000000;"> Base{ </span><span style="color: #008000;">/*</span><span style="color: #008000;">* * 魔术方法 * @param string $name * @param string $value * @return void </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __set(<span style="color: #800080;">$name</span>, <span style="color: #800080;">$value</span><span style="color: #000000;">) { </span><span style="color: #800080;">$this</span>->{<span style="color: #800080;">$name</span>} = <span style="color: #800080;">$value</span><span style="color: #000000;">; }}</span>
然后我们来实现'A,B,C'类,A类的实例 依赖于 B类的实例,B类的实例依赖于C类的实例
'A.class.php'
<span style="color: #0000ff;">class</span> A <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Base{ </span><span style="color: #0000ff;">private</span> <span style="color: #800080;">$instanceB</span><span style="color: #000000;">; </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(B <span style="color: #800080;">$instanceB</span><span style="color: #000000;">) { </span><span style="color: #800080;">$this</span>->instanceB = <span style="color: #800080;">$instanceB</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> test() { </span><span style="color: #800080;">$this</span>->instanceB-><span style="color: #000000;">test(); }}</span>
'B.class.php'
<span style="color: #0000ff;">class</span> B <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Base{ </span><span style="color: #0000ff;">private</span> <span style="color: #800080;">$instanceC</span><span style="color: #000000;">; </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(C <span style="color: #800080;">$instanceC</span><span style="color: #000000;">) { </span><span style="color: #800080;">$this</span>->instanceC = <span style="color: #800080;">$instanceC</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> test() { </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span>->instanceC-><span style="color: #000000;">test(); }}</span>
'C.class.php'
<span style="color: #0000ff;">class</span> C <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Base{ </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> test() { </span><span style="color: #0000ff;">echo</span> 'this is C!'<span style="color: #000000;">; }}de</span>
然后我们在'index.php'中获取'A'的实例,要实现自动加载,需要使用SPL类库的'spl_autoload_register'方法,代码如下
<span style="color: #0000ff;">function</span> autoload(<span style="color: #800080;">$className</span><span style="color: #000000;">){ </span><span style="color: #0000ff;">include_once</span> <span style="color: #800080;">$className</span> . '.class.php'<span style="color: #000000;">;}spl_autoload_register(</span>'autoload', <span style="color: #0000ff;">true</span>, <span style="color: #0000ff;">true</span><span style="color: #000000;">);</span><span style="color: #800080;">$container</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Container;</span><span style="color: #800080;">$a</span> = <span style="color: #800080;">$container</span>->get('A'<span style="color: #000000;">);</span><span style="color: #800080;">$a</span>->test();<span style="color: #008000;">//</span><span style="color: #008000;">输出 'this is C!'</span>
上面的例子看起来是不是很爽,根本都不需要考虑'B','C' (当然,这里B,C 除了要使用相应类的实例外,没有其他参数,如果有其他参数,必须显要调用'$container->set(xx)'方法进行注册,为其制定实例化必要的参数)。有细心同学可能会思考,比如我在先获取了'A'的实例,我在另外一个地方也要获取'A'的实例,但是这个地方'A'的实例需要其中某个属性不一样,我怎么做到?
你可以看到'Container' 类的 'get' 方法有其他两个参数,'$params' 和 '$properties' , 这个'$properties' 即可实现刚刚的需求,这都依赖'__set'魔术方法,当然这里你不仅可以注册类,也可以注册方法或者对象,只是注册方法时要使用回调函数,例如
<span style="color: #800080;">$container</span>->set('foo', <span style="color: #0000ff;">function</span>(<span style="color: #800080;">$container</span>, <span style="color: #800080;">$params</span>, <span style="color: #800080;">$config</span><span style="color: #000000;">){ </span><span style="color: #008080;">print_r</span>(<span style="color: #800080;">$params</span><span style="color: #000000;">); </span><span style="color: #008080;">print_r</span>(<span style="color: #800080;">$config</span><span style="color: #000000;">);});</span><span style="color: #800080;">$container</span>->get('foo', <span style="color: #0000ff;">array</span>('name' => 'foo'), <span style="color: #0000ff;">array</span>('key' => 'test'));
还可以注册一个对象的实例,例如
<span style="color: #0000ff;">class</span><span style="color: #000000;"> Test{ </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> mytest() { </span><span style="color: #0000ff;">echo</span> 'this is a test'<span style="color: #000000;">; }}</span><span style="color: #800080;">$container</span>->set('testObj', <span style="color: #0000ff;">new</span><span style="color: #000000;"> Test());</span><span style="color: #800080;">$test</span> = <span style="color: #800080;">$container</span>->get('testObj'<span style="color: #000000;">);</span><span style="color: #800080;">$test</span>->mytest();
以上自动加载,依赖控制的大体思想就是将类所要引用的实例通过构造函数注入到其内部,在获取类的实例的时候通过PHP内建的反射解析构造函数的参数对所需要的类进行加载,然后进行实例化,并进行缓存以便在下次获取时直接从内存取得
以上代码仅仅用于学习和实验,未经严格测试,请不要用于生产环境,以免产生未知bug
鄙人才疏学浅,有不足之处,欢迎补足!
- 4楼玻璃鱼儿
- PHP还是适合过程化程序编写,语言特征决定的。
- Re: 八面碰壁居士
- @玻璃鱼儿,恩,面向过程看起来比较简单,有不少优势。不过当项目大时,就会越来越乱。通常将面向过程和对象结合起来比较好!
- 3楼freephp
- 好复杂,特别是后面实现自动加载的。能不能简化一下代码?
- Re: 八面碰壁居士
- @freephp,恩,好的,我将抽时间对上面每步进行拆解并进行解释
- 2楼batsing
- 后面的 spl_autoload_register 不是直接用 __autoload 更简化吗
- 1楼卓酷
- 我怎么觉得还是改动一下mail类更方便呢?后面又是接口又是依赖注入的,绕来绕去都懵了。,感觉对于PHP这样的纯粹动态脚本语言来加讲,不要把它当作静态语言来用,否则有很多的特性不但发挥不出来。
- Re: 八面碰壁居士
- @卓酷,当然这里只是阐述这个思想,如果仅仅改动mail类,在需求不断增加和改动的情况下会使该类十分臃肿,并且产生很多if语句,不利于维护。至于性能问题,我觉得在当前实际情况下瓶颈不在于php,而更多的在于网络和存储方面。性能论不是绝对的,要和开发维护成本进行比较,当前例子极大的降低了维护成本,而带来的性能问题微乎其微甚至在某些情况下还有提升,此处缓冲了类的实例,减少了大部分开销

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

类和方法的概念和实例类(Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。方法:类中定义的函数。类的构造方法__init__():类有一个名为init()的特殊方法(构造方法),该方法在类实例化时会自动调用。实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用self修饰的变量。实例化:创建一个类的实例,类的具体对象。继承:即一个派生类(derivedclass)继承基类(baseclass)的

function是函数的意思,是一段具有特定功能的可重复使用的代码块,是程序的基本组成单元之一,可以接受输入参数,执行特定的操作,并返回结果,其目的是封装一段可重复使用的代码,提高代码的可重用性和可维护性。

class是python中的一个关键字,用来定义一个类,定义类的方法:class后面加一个空格然后加类名;类名规则:首字母大写,如果多个单词用驼峰命名法,如【class Dog()】。

jQuery是一种经典的JavaScript库,被广泛应用于网页开发中,它简化了在网页上处理事件、操作DOM元素和执行动画等操作。在使用jQuery时,经常会遇到需要替换元素的class名的情况,本文将介绍一些实用的方法,以及具体的代码示例。1.使用removeClass()和addClass()方法jQuery提供了removeClass()方法用于删除

标题:C#中使用Array.Sort函数对数组进行排序的示例正文:在C#中,数组是一种常用的数据结构,经常需要对数组进行排序操作。C#提供了Array类,其中有Sort方法可以方便地对数组进行排序。本文将演示如何使用C#中的Array.Sort函数对数组进行排序,并提供具体的代码示例。首先,我们需要了解一下Array.Sort函数的基本用法。Array.So

在编写PHP代码时,使用类(Class)是一个非常常见的做法。通过使用类,我们可以将相关的功能和数据封装在一个单独的单元中,使代码更加清晰、易读和易维护。本文将详细介绍PHPClass的用法,并提供具体的代码示例,帮助读者更好地理解如何在实际项目中应用类来优化代码。1.创建和使用类在PHP中,可以使用关键字class来定义一个类,并在类中定义属性和方法。

Vue报错:无法正确使用v-bind绑定class和style,怎样解决?在Vue开发中,我们经常会用到v-bind指令来动态绑定class和style,但是有时候我们可能会遇到一些问题,如无法正确使用v-bind绑定class和style。在本篇文章中,我将为你解释这个问题的原因,并提供解决方案。首先,让我们先了解一下v-bind指令。v-bind用于将V

在本文中,我们将了解enumerate()函数以及Python中“enumerate()”函数的用途。什么是enumerate()函数?Python的enumerate()函数接受数据集合作为参数并返回一个枚举对象。枚举对象以键值对的形式返回。key是每个item对应的索引,value是items。语法enumerate(iterable,start)参数iterable-传入的数据集合可以作为枚举对象返回,称为iterablestart-顾名思义,枚举对象的起始索引由start定义。如果我们忽
