🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。 >[danger]将多个类对象统一到facade类的某个方法中输出 外观(Facade)模式是“迪米特法则”的典型应用, **主要优点。** 1. 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。 2. 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。 3. 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。 **主要缺点。** 1. 不能很好地限制客户使用子系统类。 2. 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。 **模式的结构** 外观(Facade)模式包含以下主要角色。 1. 外观(Facade)角色:为多个子系统对外提供一个共同的接口。 2. 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。 3. 客户(Client)角色:通过一个外观角色访问各个子系统的功能。 ![](https://img.kancloud.cn/4d/35/4d35a07a7ca50fe0643a6e54e6019007_509x539.png) ``` //外观角色 class Facade { private $obj1; private $obj2; private $obj3; public function __construct(){ $this->obj1=new SubSystem01(); $this->obj2=new SubSystem02(); $this->obj3=new SubSystem03(); } public function method() { $this->obj1->method1(); $this->obj2->method2(); $this->obj3->method3(); } } //子系统角色1 class SubSystem01 { public function method1() { echo "子系统01的method1()被调用!<br>"; } } //子系统角色2 class SubSystem02 { public function method2() { echo "子系统02的method2()被调用!<br>"; } } //子系统角色3 class SubSystem03 { public function method3() { echo "子系统03的method3()被调用!<br>"; } } class Client { public static function main() { $f=new Facade(); $f->method(); } } Client::main(); 结果: 子系统01的method1()被调用! 子系统02的method2()被调用! 子系统03的method3()被调用! ``` ## **外观模式的应用场景** 通常在以下情况下可以考虑使用外观模式。 1. 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。 2. 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。 3. 当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。 ## **外观模式的扩展** 在外观模式中,当增加或移除子系统时需要修改外观类或客户端的源代码,这违背了“开闭原则”。如果引入抽象外观类,则在一定程度上解决了该问题, 在引入抽象外观类之后,客户端可以针对抽象外观类进行编程,对于新的业务需求,不需要修改原有外观类,而对应增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,同时通过修改配置文件来达到不修改任何源代码并更换外观类的目的。 其结构图如图所示。 ![](https://img.kancloud.cn/a8/58/a8581ae250de2bc82dddba75f3f30c5a_540x532.png) ``` //子系统角色1 class SubSystem01 { public function method1() { echo "子系统01的method1()被调用!<br>"; } } //子系统角色2 class SubSystem02 { public function method2() { echo "子系统02的method2()被调用!<br>"; } } //子系统角色3 class SubSystem03 { public function method3() { echo "子系统03的method3()被调用!<br>"; } } //子系统角色4 class SubSystem04 { public function method4() { echo "子系统04的method4()被调用!<br>"; } } abstract class AbstractFacade{ abstract public function method1(); abstract public function method2(); } //原外观角色类 class Facade1 extends AbstractFacade { private $obj1; private $obj2; private $obj3; public function __construct(){ $this->obj1=new SubSystem01(); $this->obj2=new SubSystem02(); $this->obj3=new SubSystem03(); } public function method1() { $this->obj1->method1(); $this->obj2->method2(); $this->obj3->method3(); } public function method2(){} } //添加或者删除子系统角色而新增加外观角色类 class Facade2 extends AbstractFacade { private $obj2; private $obj3; private $obj4; public function __construct(){ $this->obj2=new SubSystem02(); $this->obj3=new SubSystem03(); $this->obj4=new SubSystem04(); } public function method1(){ $this->obj2->method2(); $this->obj3->method3(); $this->obj4->method4(); } public function method2(){} } class Client { public static function main() { $f=new Facade1(); $f->method1(); //当移除子系统角色1,添加子系统角色4时,我们只需要重新定义Facade2即可 $f=new Facade2(); $f->method1(); } } Client::main(); ``` 结果: ``` 子系统01的method1()被调用! 子系统02的method2()被调用! 子系统03的method3()被调用! 子系统02的method2()被调用! 子系统03的method3()被调用! 子系统04的method4()被调用! ``` **文件加密的门面模式例子** ``` //读取并返回配置文件的内容 function config($param){ //这里就不读取配置文件,简单返回测试下 return "CipherMachine"; } //抽象门面类 abstract class AbstractEncryptFacade { abstract public function FileEncrypt(string $fileNameSrc, string $fileNameDes); } // 旧加密类 class CipherMachine{ public function Encrypt($plainText){ //省略加密规则 $encryptText="旧加密类加密后的字符串"; return $encryptText; } } // 原门面类 // 新的门面类 class EncryptFacade extends AbstractEncryptFacade { //文件源内容 private $plainStr; //加密对象 private $cipher; //文件加密后的内容 private $encryptStr; public function EncryptFacade() { $this->cipher = new CipherMachine(); //其他对象 //... ... } public function FileEncrypt(string $fileNameSrc, string $fileNameDes) { //读取文件内容 $this->plainStr = file_get_contents($fileNameSrc); //返回加密后的文件内容 $this->encryptStr = $this->cipher->Encrypt($this->plainStr); //将加密文件内容写入文件中 file_put_contents($fileNameDes,$this->encryptStr); } } // 新加密类 class NewCipherMachine{ public function Encrypt($plainText){ //省略加密规则 $encryptText="新加密类加密后的字符串"; return $encryptText; } } // 新的门面类 class NewEncryptFacade extends AbstractEncryptFacade { //文件源内容 private $plainStr; //加密对象 private $cipher; //文件加密后的内容 private $encryptStr; public function NewEncryptFacade() { $this->cipher = new NewCipherMachine(); } public function FileEncrypt(string $fileNameSrc, string $fileNameDes) { //读取文件内容 $this->plainStr = file_get_contents($fileNameSrc); //返回加密后的文件内容 $this->encryptStr = $this->cipher->Encrypt($this->plainStr); //将加密文件内容写入文件中 file_put_contents($fileNameDes,$this->encryptStr); } } class Client{ public static function main(){ //读取配置文件 $conf=config("encrypt.type"); //根据配置文件的值加密文件 if ($conf=="CipherMachine") { (new EncryptFacade)->FileEncrypt("./test.txt","newtest.txt"); }else{ (new NewEncryptFacade)->FileEncrypt("./test.txt","newtest.txt"); } } } Client::main(); ``` **例子:tp的门面模式** ``` namespace app\admin; class Test { public function hello($name) { return '你好啊,' . $name; } } namespace think; /** * Facade管理类 */ class Facade { /** * 始终创建新的对象实例 * @var bool */ protected static $alwaysNewInstance; /** * 创建Facade实例 * @static * @access protected * @param string $class 类名或标识 * @param array $args 变量 * @param bool $newInstance 是否每次创建新的实例 * @return object */ protected static function createFacade(string $class = '', array $args = [], bool $newInstance = false) { $class = $class ?: static::class;//app\facade\Test $facadeClass = static::getFacadeClass();//app\facade\Test 即Test子类的getFacadeClass返回的结果 if ($facadeClass) { $class = $facadeClass; } if (static::$alwaysNewInstance) { $newInstance = true; } //Container::getInstance() 实例化Container类 make()方法则实例化传入的类并将此类对象挂载到instance属性上(注册树模式?) //返回 实例化成功的类对象这里是指app\facade\Test return Container::getInstance()->make($class, $args, $newInstance); } /** * 获取当前Facade对应类名 * @access protected * @return string */ protected static function getFacadeClass() {} /** * 带参数实例化当前Facade类 * @access public * @return object */ public static function instance(...$args) { if (__CLASS__ != static::class) { return self::createFacade('', $args); } } /** * 调用类的实例 * @access public * @param string $class 类名或者标识 * @param array|true $args 变量 * @param bool $newInstance 是否每次创建新的实例 * @return object */ public static function make(string $class, $args = [], $newInstance = false) { if (__CLASS__ != static::class) { return self::__callStatic('make', func_get_args()); } if (true === $args) { // 总是创建新的实例化对象 $newInstance = true; $args = []; } return self::createFacade($class, $args, $newInstance); } // 调用实际类的方法 调用一个不存在或者不能访问的静态方法时触发此方法 public static function __callStatic(string $method, array $params) { //$method为调用的不存在的那个方法, $params则是不存在这个方法的参数 return call_user_func_array([static::createFacade(), $method], $params); } } namespace app\facade; use think\Facade; class Test extends Facade { protected static function getFacadeClass() { return 'app\admin\Test'; } } //通常 $test = new \app\admin\Test; echo $test->hello('thinkphp框架'); // 输出 你好啊,thinkphp框架 // 使用facade就 无需进行实例化 直接以静态方法方式调用hello方法 echo \app\facade\Test::hello('thinkphp框架'); ```