作者banjmin (HD)
看板PHP
标题Re: [请益] Dependency Injection 疑问
时间Tue Jun 9 22:08:50 2015
你要设计一个方法,要能通过floppy bird的关卡
一种鸟只能飞低、超低、高、超高
假设你写了一个这样只能飞高的鸟的class
class FlyHighBird
{
public function fly()
{
return "fly high";
}
}
但是floppy bird的关卡的class中
Class Stage
{
private $bird;
private $interspace;
function __construct()
{
$this->bird = new FlyHighBird();
}
function check()
{
if($this->bird->fly() == $interspace)
return true;
return false;
}
// other method dynamically change $interspace
}
Stage在编译时期就决定依赖於Bird物件
那麽你这只只能飞高的鸟,势必过不了关卡现在空隙位置是
低、超低、或超高的检查
那麽要能一直通过不同高度的关卡检查,
你势必要在runtime改变Stage依赖的物件才行
其实Stage根本不care你是哪种鸟,甚至根本不care你是不是鸟,
他只在乎你是飞的高还是飞的低,也就是只在乎你的
"飞的行为",你手边有四种鸟都只能飞不同的高度,那麽你只要能动态改变Stage依赖
的$bird物件中的实作,似乎就能通过每一次的检查
所以你把"飞的行为"从四种鸟的class中一般化成介面
interface FlyBehavior
{
public function fly();
}
四种鸟分别实作这个介面完成四种不同高度的飞行行为
改变Stage相依的物件
class Stage
{
private $flyBehavior;
private $interspace;
function setFlyBehavior($flyBehavior)
{
$this->flyBehavior = $flyBehavior;
}
function check()
{
if($this->flyBehavior->fly() == $interspace)
return true;
return false;
}
//other methods
}
现在Stage 没有在编译时期依赖於某一种鸟类了,而是透过FlyBehavior介面
从Stage的外部注入其中一种鸟类的实作,来动态透过setFlyBehavior的替换目前
依赖的实作,这种依赖关系从原本自己物件内部,到被抽到外部决定再透过setter
或建构子注入就是依赖注入,能做到runtime才根据一些参数
(比如说Stage有个方法getInterspace()提供给你$interspace的数值,再在外部加以判断)
决定要注入哪一种实作,来达到通过每一次检查的弹性
将来floppy bird改版了,要检查是否到达"宇宙的高度",才算通过
你只要打造一个火箭的Class,一样完成FlyBehavior的实作,让他飞到宇宙的高度
再注入到Stage物件就能通过关卡了,而你"并没有修改Stage一开始相依物件的程式码"
因为Stage原本直接依赖於某种鸟类的实作,这样的关系被"介面"decoupling了
也就是"针对介面写程式,不要针对实作写程式"的OO守则
配合DI的方式能做到更多面对需求变更时,还能保有弹性,你要修改的程式少了很多
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 125.224.178.103
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/PHP/M.1433858945.A.BDA.html
※ 编辑: banjmin (125.224.178.103), 06/09/2015 22:12:44
1F:推 tkdmaf: 甚至不care你是不是鸟XDD... 06/10 00:56
2F:推 Den3: 想请问这是为了解决不要重新编译的问题,那PHP这种直译的情 06/10 08:46
3F:→ Den3: 况下也适用吗? 06/10 08:46
4F:→ Den3: 没事,不好意思,刚刚短路,搞错重点 06/10 09:14
5F:推 y2468101216: 推好文应该要M 06/10 09:23
6F:推 chan15: 谢谢大大的热情回应,原理跟使用方式我懂,我想问的是情境 06/10 13:15
7F:→ chan15: 以一般继承的 class 来讲,功能可能多半集中在自己 06/10 13:16
8F:→ chan15: 有些共同 function 去 parent 拿,这是一般的配置 06/10 13:16
9F:→ chan15: DI 的设计是把功能在 Stage 操作,注入不同 class 06/10 13:17
10F:→ chan15: 换 class 等於换 config,而且是有 function 的 config 06/10 13:18
11F:→ chan15: 怎样的情境才有使用的绝对差异呢,举例一个问题 06/10 13:20
14F:→ chan15: 这两个结果一样,abstract 甚至可以继承 parent 东西来用 06/10 13:42
15F:→ chan15: 所以我想问这个原则跟使用情境 06/10 13:43
16F:→ banjmin: 重点还是要看需求多复杂,你的例子太小了,其实没什麽差 06/10 22:50
17F:→ banjmin: 差别可能就是假设你今天面对新需求势必要继承另一个class 06/10 22:50
18F:→ banjmin: 语言没有多重继承的时候 原本继承的做法就做不下去了 06/10 22:51
19F:→ banjmin: 你势必要换成介面的做法 06/10 22:51
20F:→ banjmin: abstract class用的好的例子 可以看看template pattern 06/10 22:53
21F:→ banjmin: 满足开放封闭原则,看看decorator pattern怎麽使用DI 06/10 23:02
22F:→ banjmin: 不过不管pattern怎麽样,重点还是你想做什麽功能 06/10 23:02
23F:→ banjmin: 再来谈适合的、有弹性的设计,pattern例子通常不能直接套 06/10 23:03