假设有商人开水果摊。
我们把水果摊用如下类表示:
class FruitStand
{
}
水果摊会贩卖很多不同的水果,因为每一种水果都有独特的属性,所以每一种水果我们也可以单独用类表示。
比如:苹果
class Apple
{
protected $weight; //重量
protected $sweet; //甜度
public function __construct($weight,$sweet)
{
$this->weight = $weight;
$this->sweet = $sweet;
}
}
再比如:柠檬
class Lemon
{
protected $weight; //重量
protected $acid; //酸度
public function __construct($weight,$acid)
{
$this->weight = $weight;
$this->acid = $acid;
}
}
现在,一个水果摊开张了。
class FruitStand
{
protected $Fruits = []; //存放水果
public function __construct()
{
//水果摊有这些水果
$this->Fruits = array(
new Apple(2,100),
new Lemon(3,10)
);
}
}
这个时候就能发现依赖产生了。
什么是依赖:就是 “我若依赖你,少了你就没有我”。
由代码可以知道,若修改了class Apple 和 class Lemon ,则 class FruitStand 无法正常运行。若想class FruitStand 正常运行,则需要修改 class FruitStand。且当新增一种水果时,class FruitStand 的修改一样不可避免。
意思就是,如果苹果新增了一项属性,比如产地,则牵扯到水果摊也得整改。
class FruitStand
{
protected $Fruits = []; //存放水果
public function __construct()
{
//水果摊有这些水果
$this->Fruits = array(
//new Apple(2,100), 苹果没有了,需要修改此类
new Lemon(3,10,111), //变更了柠檬的属性,需要改动此类
new Pear(), //新增了梨,需要修改此类
//... //更多改动
);
}
}
有上述代码可以知道,每次商人想要改动某个水果的属性时,都需要重新修改水果摊。
显而易见,这是不对的。
我们不应该在水果摊内部固定了水果有哪些,而转由外部负责。
这个时候,工厂模式就应运而生,把水果摊的水果种类问题,交于工厂去创造。
就好比,不需要我们自己动手去new 每一种水果,而交于工厂帮我们创造需要依赖的水果。而我们只需要告诉工厂,我们依赖哪些水果。
这种由外部负责其依赖需求的行为,我们可以称其为 控制反转(IoC)
水果摊加工工厂:
/**
* 水果摊创造工厂
*/
class FruitStandFactory
{
//工厂创造水果摊的方法
public function make($fruits,$options)
{
switch ($fruits) {
case 'Apple': return new Apple($options[0], $options[1]); //创造苹果
case 'Lemon': return new Lemon($options[0], $options[1]); //创造柠檬
case 'Pear': return new Pear($options[0], $options[1], $options[2]); //创造梨
}
}
}
有了水果摊创造工厂,我们创建水果摊时就这样子:
/**
* 水果摊类
*/
class FruitStand
{
protected $fruits = []; //存放水果
public function __construct(array $fruits) //参数 $fruits 表示告诉工厂生产的水果摊需要哪些水果
{
//叫来工厂帮忙
$factory = new FruitStandFactory;
//通过工厂提供的方法创造需要的水果摊
foreach ($fruits as $name => $options) {
$this->fruits[] = $factory->make($name, $options);
}
}
}
// 创建水果摊
$fruitStand = new FruitStand([
'Apple' => [1, 2],
'Lemon' => [3, 4]
]);
我们如果需要改了某个水果类的属性,只需要在FruitStandFactory水果摊类修改或添加新的水果就可以了。也就是说我们不需要自己动手去修改水果摊,而都交由工厂去修改并生产。
但是,这才刚刚开始。
本来FruitStand依赖多个水果类(Apple,Lemon),变成了FruitStand依赖一个FruitStandFactory水果摊工厂类。
当水果类型增多时,水果工厂依然要进行修改。
/**
* 水果摊-创造工厂
*/
class FruitStandFactory
{
//工厂创造水果摊的方法
public function make($fruits,$options)
{
switch ($fruits) {
case 'Apple': return new Apple($options[0], $options[1]); //创造苹果
case 'Lemon': return new Lemon($options[0], $options[1]); //创造柠檬
case 'Pear': return new Pear($options[0], $options[1], $options[2]); //创造梨
//case 'xxx'
//case 'xxxx'
//case 'xxxxx'
//...
}
}
}
这个时候就来了,依赖注入。
只要不是由内部生产(比如初始化、构造函数__construct
中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于依赖注入(DI)
。
class FruitStand
{
protected $fruit = [];
public function __construct(array $fruits)
{
foreach ($fruits as $fruit ) {
$this->fruit[] = $fruit;
}
}
}
// 创建水果摊
$Apple = new Apple(1,2);
$Lemon = new Lemon(3,4);
$fruitStand = new FruitStand([
$Apple,$Lemon
]);
这个时候,如果我们新增了一个水果,只需要将新依赖的水果最为参数传递进去,而不需要修改任何类。
// 创建水果摊
$Apple = new Apple(1,2);
$Lemon = new Lemon(3,4);
$Pear = new Pear(5); //新增了梨
$fruitStand = new FruitStand([
$Apple,$Lemon,$Pear
]);
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。