PHP 工厂模式,依赖注入与控制反转

php基础

浏览数:86

2020-6-26

假设有商人开水果摊。
我们把水果摊用如下类表示:

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
]);

作者:如若时光萧瑟去丶