在实现用户登录功能时,使用普通的new对象方法使工作类(User)对工具类(Db,Validate,View)产生了严重的依赖关系及耦合问题。虽然可以使用依赖注入解决耦合问题,但必须要对工具类的方法或参数了解,很容易造成意想不到的问题出现。
容器注入可以最大限度的简化外部对象调用,即创建容器、服务注册、容器依赖,但容器依赖必须使用User()工作类进行调用,灵活性不高。因此使用facade门面外观模式与容器注入相结合,即对调用者来说,他不需要事先知道工作类(User)中具体操作是哪个类中,只需要调用Facade即可完成操作,并且facade门面静态化方法较纯容器注入代码执行效率更高。
实例代码如下:
〓 依赖容器
◇ 创建Demo1.php:普通的new对象方法
<?php
/**
* 用户登录
* 1. 涉及到三个操作:链接数据、数据验证、模板输出
* 2. 创建三个类:Db,validate,view:这三个类是工具类
* 3. User类负责调用工具类,实现用户登录:是工作类
*/
//数据库链接类
class Db
{
//数据连接
public function connect()
{
return '数据库链接成功!<br>';
}
}
//数据验证类
class Validate
{
//数据验证
public function check()
{
return '数据验证成功!<br>';
}
}
//视图类
class View
{
//内容输出
public function display()
{
return '用户登录成功!<br>';
}
}
//用户类:工作类
class User
{
//登录操作
public function login()
{
//实例化Db类,并调用connect()方法,连接数据库
$db = new Db();
echo $db->connect();
//实例化Validate类,并调用check()方法,验证数据
$validate = new Validate();
echo $validate->check();
//实例化View类,并调用display()方法,完成用户登录
$view = new View();
echo $view->display();
}
}
//客户端进行调用
//实例化User类
$user = new User();
echo $user->login();
//工作类User对三个工具类:Db,Validate,View产生了严重的依赖,并且要求不仅要对User类熟悉,还要对工具类十分了解,这种耦合如何解耦?
◇ 创建Demo2.php:依赖注入解耦
<?php
/**
* 使用依赖注入来进行解耦:DI
* 对工具类(Db,Validate,View)的实例化不要在工作类(User)中完成,应在客户端完成
* 在工作类中必须要有接收器,来保存工具类的实例
* 将工作类依赖的对象,以方法参数的方式注入到当前的类中
*/
//数据库链接类
class Db
{
//数据连接
public function connect()
{
return '数据库链接成功!<br>';
}
}
//数据验证类
class Validate
{
//数据验证
public function check()
{
return '数据验证成功!<br>';
}
}
//视图类
class View
{
//内容输出
public function display()
{
return '用户登录成功!<br>';
}
}
//用户类:工作类
class User
{
//创建三个接收器,成员属性
protected $db = null; //保存数据对象
protected $validate = null; //保存数据验证对象
protected $view = null; //保存视图对象
//登录操作
public function login(Db $db, Validate $validate, View $view)
{
//实例化Db类,并调用connect()方法,连接数据库
echo $db->connect();
//实例化Validate类,并调用check()方法,验证数据
echo $validate->check();
//实例化View类,并调用display()方法,完成用户登录
echo $view->display();
}
}
//客户端进行调用
//将工具类在客户端全部实例化
$db = new Db();
$validate = new Validate();
$view = new View();
////实例化User类
$user = new User();
echo '<h2>用依赖注入完成解耦</h2>';
echo $user->login($db,$validate,$view);
/**
* 依赖注入仍然要求在客户端进行类的实例化
* 遗留问题:还是无法解决对工具类需要十分了解的问题
* 期望:能不能把这个调用者的实例化过程也省略掉,我们只要给出一个类名,以及如何去实例化这个类就可以了,其他工作一概不管
* 解决办法:用容器来实现统一注入,文件在Demo3.php
*/
◇ 创建Demo3.php:容器依赖解耦
<?php
/**
* 容易:也叫服务容器(IOC)
* 基本思路:拿来就用,最大限度简化外部对象调用,类似于 即插即用
* 基本实现分三步:
* 1. 创建容器:本质就是将一个类与他的实现绑定到一个关联数组;
* 2. 服务注册:初始化这个关联数组,将工具类绑定到容器中;
* 3. 容器依赖:也叫依赖容器,调用的时候直接传一个容器对象,不用再一个一个传具体对象
*/
//数据库链接类
class Db
{
//数据连接
public function connect()
{
return '数据库链接成功!<br>';
}
}
//数据验证类
class Validate
{
//数据验证
public function check()
{
return '数据验证成功!<br>';
}
}
//视图类
class View
{
//内容输出
public function display()
{
return '用户登录成功!<br>';
}
}
//1. 创建容器
class Container
{
//创建一个空数组,用来保存工具类及实现方法
protected $instance = [];
//将需要实例化的类与它的实现方法进行绑定
public function bind($abstract, Closure $process)
{
$this->instance[$abstract] = $process;
}
//创建特定类的实例
public function make($abstract, $process=[])
{
return call_user_func_array($this->instance[$abstract], []); //执行对象方法
}
}
//2. 服务注册:就是调用容器的bind()将对象(对象===实例)注册到容器中
$container = new Container();
//将Db类绑定到容器中
$container->bind('Db', function(){
return new Db();
});
//将Validate类绑定到容器中
$container->bind('Validate', function(){
return new Validate();
});
//将View类绑定到容器中
$container->bind('View', function(){
return new View();
});
// var_dump($container->instance);die(); //查看容器的instance属性,测试是否成功
//3. 容器依赖:所有用到的对象,以容器的方式,注入到当前的工作类中
//用户类:工作类
class User
{
public function login(Container $container)
{
//$container->make('Db')实例化Db类创建Db对象
echo $container->make('Db')->connect();
echo $container->make('Validate')->check();
echo $container->make('View')->display();
}
}
//控制台调用方法
$user = new User();
echo '<h2>用依赖容器实现解耦</h2>';
echo $user->login($container);
//问题遗留:容器依赖必须使用User()工作类进行调用,我们用facade门面模式进行简化,见Demo4.php
〓 Facade门面外观模式
◇ 创建Demo4.php和Container.php:容器依赖结合facade解耦
Demo4.php-----------------------------------------------------
<?php
/**
* facade:门面模式,也叫外观模式
* 就是将一些操作进行封装,对外提供一个统一的接口
* facade模式与容器依赖是黄金搭档
*/
require 'Container.php'; //导入容器
/**
* Facade类实现三个功能
* 1. 链接数据库
* 2. 数据验证
* 3. 输出提示
* 对于调用者来说,他不需要实现知道这三个操作在哪个具体的类中,他只需要调用Facade就可以执行以上三个操作
*/
class Facade
{
// 1. 链接数据库
public static function connect(Container $container)
{
return $container->make('Db')->connect();
}
// 2. 数据验证
public static function check(Container $container)
{
return $container->make('Validate')->check();
}
// 3. 输出提示
public static function display(Container $container)
{
return $container->make('View')->display();
}
}
//客户端调用
echo '<h2>用facade门面外观模式统一调用User类</h2>';
echo Facade::connect($container);
echo Facade::check($container);
echo Facade::display($container);
Container.php----------------------------------------------------
<?php
/**
* 容易:也叫服务容器(IOC)
* 基本思路:拿来就用,最大限度简化外部对象调用,类似于 即插即用
* 基本实现分三步:
* 1. 创建容器:本质就是将一个类与他的实现绑定到一个关联数组;
* 2. 服务注册:初始化这个关联数组,将工具类绑定到容器中;
* 3. 容器依赖:也叫依赖容器,调用的时候直接传一个容器对象,不用再一个一个传具体对象
*/
//数据库链接类
class Db
{
//数据连接
public function connect()
{
return '数据库链接成功!<br>';
}
}
//数据验证类
class Validate
{
//数据验证
public function check()
{
return '数据验证成功!<br>';
}
}
//视图类
class View
{
//内容输出
public function display()
{
return '用户登录成功!<br>';
}
}
//1. 创建容器
class Container
{
//创建一个空数组,用来保存工具类及实现方法
protected $instance = [];
//将需要实例化的类与它的实现方法进行绑定
public function bind($abstract, Closure $process)
{
$this->instance[$abstract] = $process;
}
//创建特定类的实例
public function make($abstract, $process=[])
{
return call_user_func_array($this->instance[$abstract], []); //执行对象方法
}
}
//2. 服务注册:就是调用容器的bind()将对象(对象===实例)注册到容器中
$container = new Container();
//将Db类绑定到容器中
$container->bind('Db', function(){
return new Db();
});
//将Validate类绑定到容器中
$container->bind('Validate', function(){
return new Validate();
});
//将View类绑定到容器中
$container->bind('View', function(){
return new View();
});
◇ 创建Demo5.php: 进一步简化facade门面外观模式
<?php
/**
* facade门面外观模式简化
*
*/
require 'Container.php'; //导入容器
class Facade
{
//保存容器对象
protected static $container = null;
//创建初始化方法,给容器对象赋值
public static function initalize(Container $container)
{
static::$container = $container;
}
// 1. 链接数据库
public static function connect()
{
return static::$container->make('Db')->connect();
}
// 2. 数据验证
public static function check()
{
return static::$container->make('Validate')->check();
}
// 3. 输出提示
public static function display()
{
return static::$container->make('View')->display();
}
}
//客户端调用
echo '<h2>简化了的facade门面外观模式调用</h2>';
Facade::initalize($container);
echo Facade::connect();
echo Facade::check();
echo Facade::display();