不知道大家看找源码中的设计模式是否会看看每个包或者类的名字来猜这快代码用了什么设计模式,其实确实是有根据的,名字是个设计模式,十有八九就是用了那个设计模式。像这里的support这个包里面放置了一个Facades包,这不就是门面模式的英文吗,确实这里面放置的类都是一些门面类,这些门面类都继承于同一个父类Facade.php。
文件路径:vendor\laravel\framework\src\Illuminate\Support\Facades
Facades包为应用程序的服务容器中可用的类提供了一个静态接口。Laravel 自带了许多的 facades,可以用来访问其几乎所有的服务,这些服务类都由一个公共父类就是Facade。Laravel facades 就是服务容器里那些基类的静态代理,相比于传统的静态方法调用,facades 在提供更简洁且丰富的语法的同时,还有更好的可测试性和扩展性。
在config\app.php中会有一个数组providers,注册了一个服务提供者,比如说Illuminate\Filesystem\FilesystemServiceProvider::class。在 alias 数组中定义了一个门面,比如说‘File’ => Illuminate\Support\Facades\File::class。通过这两个步骤,我们就可以非常方便的使用 Laravel 提供的文件系统相关的操作。
相关路径:Illuminate\Support\Facades\File
//父类Facade.php中的getFacadeAccessor()方法
protected static function getFacadeAccessor()
{
//返回实际的对象类名,获取类对象。每个类对象一旦创建,就放在一个静态数组中,因此在一次请求中最多只会被创建一次。
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}
//子类重写父类中的getFacadeAccessor()方法
class File extends Facade
{
protected static function getFacadeAccessor()
{
return 'files';
}
}
相关路径:Illuminate\Filesystem\FilesystemServiceProvider
Illuminate\Filesystem\Filesystem
具体的门面类如File,提供给用户调用。都继承与父类Facade,实现了getFacadeRoot方法,获取门面背后的真实对象。同时重写了getFacadeAccessor方法,返回实际的对象类名,获取类对象。每个类对象一旦创建,就放在一个静态数组中,因此在一次请求中最多只会被创建一次。在服务提供者中比如FilesystemServiceProvider通过真实对象类名会创建一个具体服务对象Filesystem 。这样一来,就可以直接使用 File 这个门面,来调用这个 Filesystem 实例中的方法了,像File::exists($name)这些都是调用了Filesystem实例中的exists方法,File门面类里根本没有这个方法。
//具体类,以下是FilesystemServiceProvider类中的注册方法
protected function registerNativeFilesystem()
{
$this->app->singleton('files', function () {
return new Filesystem;
});
}
//Filesystem类中的其中一个方法
public function exists($path)
{
return file_exists($path);
}
如果不用这个模式,在一个庞大的系统中,充满了许多的子系统和子模块协作,而外部对于子系统的调用会形成非常大的耦合。这样的耦合显然就违背了我们拆解系统的初衷,也让系统拆分失去了意义。而外观模式其实就是为了将调用和框架解耦,减少系统中的相互依赖。
类图:
时序图: