【php】简单工厂模式、依赖注入

业务场景:我们每天需要准时到公司上班下班,可以搭乘不同交通工具。

在不使用设计模式时,通常可以这样写:

//通勤
class Commute{

    //上班
    public function gotowork(){
        switch ($way) {
            case 'bus':
                return '乘坐巴士上班';
            default:
                return '乘坐地铁上班';
        }
    }
    //下班
    public function gohome(){
        switch ($way) {
            case 'bus':
                return '乘坐巴士下班';
            default:
                return '乘坐地铁下班';
        }
    }
}

以后需要新增通勤方式、修改某种通勤方式,都需要修改Commute类,此类的维护随着业务增加变得复杂。

由此引入工厂模式:

不管使用简单工厂模式还是依赖注入,我们首先需要定义交通工具。

1.定义交通工具接口、类 

//交通工具
interface Vehicle{

    function travel();
}

//巴士
class Bus implements Vehicle{

    public function travel(){
        return "巴士";
    }
}

//地铁
class Metro implements Vehicle{

    public function travel(){
        return "地铁";
    }
}

2.一个生产乘坐不同交通方式的工厂

//普通工厂模式
class Factory{

    //通勤方案:根据交通方式返回不同的对象
    public function mychoice($way){
        switch ($way) {
            case 'bus':
                return new Bus;
            default:
                return new Metro;
        }
    }

}

3.调用工厂类实现出行

class Commute{

    //此时我上班通勤方式只需修改传入Factory的参数即可更改
    public function gotowork(){
        return '乘坐'.(new Factory)->mychoice('bus')->travel().'上班</br>';
    }

    public function gohome(){
        return '乘坐'.(new Factory)->mychoice('bus')->travel().'下班</br>';
    }
}

Commute只需关注要乘坐bus上班,不关心bus具体实现。

echo (new Commute)->gotowork();

引入工厂模式后,新增、修改都给了工厂,用户出行变得简洁。

但是新增、修改仍然需要修改工厂类,此时引入依赖注入。

以上步骤1定义交通工具接口、类仍然有用,第2步之后变为:

//通勤
class Commute{

    private $way;

    //Vehicle的意思是定义强类型:在此处的参数$car必须是遵从Vehicle契约的对象实例。如果这里参数类型强制是一个类的话,代表的是参数必须是类的实例或者是该类子类的实例
    public function __construct(Vehicle $car){
        $this->way = $car;
    }
    //此时我上班通勤方式只需修改传入Factory的参数即可更改
    public function gotowork(){
        return '乘坐'.$this->way->travel().'上班</br>';
    }

    public function gohome(){
        return '乘坐'.$this->way->travel().'下班</br>';
    }
}

Commute不再关心具体交通工具,只关心自己要乘坐交通工具上班,在调用时传入交通工具bus:

echo (new Commute(new Bus))->gotowork();

依赖注入解决了对工厂类的解耦,但是当后期需要更换增加新的交通方式,交通工具类依然需要修改、增加,此时可以引入IOC容器

IOC容器

<?php

namespace Illuminate\Redis;

use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\Arr;
use Illuminate\Support\ServiceProvider;

class RedisServiceProvider extends ServiceProvider implements DeferrableProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('redis', function ($app) {
            $config = $app->make('config')->get('database.redis', []);

            return new RedisManager($app, Arr::pull($config, 'client', 'phpredis'), $config);
        });

        $this->app->bind('redis.connection', function ($app) {
            return $app['redis']->connection();
        });
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return ['redis', 'redis.connection'];
    }
}

这段是laravel中的redis服务提供者类,在其注册方法register()中使用匿名函数将redis绑定(此次是单例绑定singleton,还有其他两种,普通绑定bind和绑定实例instance)到服务容器中,而服务容器本身是一个类,等程序真正运行时,容器可以通过反射递归地解析当前类的依赖、它的依赖的依赖并加载到容器中运行,简而言之,容器是可以注册各种接口并实现绑定的空间。这样做是为了最大程度地对依赖关系解耦。在laravel中,这个依赖关系写在\config\app.php中,\config文件夹下有一系列服务提供者对应的配置文件,它通常也可以集中读取.env文件中的配置参数。

注册方法这里为啥用匿名函数呢?

因为匿名函数可以只绑定先不运行,就是实例化RedisManager这一步可以等真正用到redis才运行,也避免了加载过多不必要的服务。

反射

反射是非常重要的概念,很多语言都有,php的反射也是相当强大,抓来一个类、对象都可以对其分析,而不用真的引用它、运行它才可以。

有说法是反射会带来性能的一定损失,我没有测试过无法下定论,不过对于laravel这样的框架来说,优雅才是第一位的,它首先要提高的是开发效率,要让代码更加整洁、扩展性更强。

可见,服务容器不仅提供了解耦,它也可以按顺序加载你的依赖、你的依赖的依赖,构建了一个安全的运行环境;并且看情况选择绑定方式和延迟加载,以节约资源。

参考:

PHP: 反射 - Manual

https://learnku.com/docs/laravel-core-concept/5.5/依赖注入,控制翻转,反射/3017

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值