外观模式就是为子系统中得一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式在现实生活中的示例很多,比如组装电脑,通常会有两种方案,一种是自己去购买需要的配件,另外一种是找一家装机公司,将具体配置提出来,然后等着拿电脑就行,虽然后者相对于前者来说费用会相对更高,但这样省去了很多麻烦事,所以大多数人还是愿意选择后者的。在我们实际开发中,经常会碰到客户端为了使用某种功能时,需要与该功能子系统内部的多个模块交互,这对于客户端而言,是个麻烦,使得客户端不能简单的使用某个功能,而且,当该功能的某个模块发送变化时,可能也会引起客户端也要随着变化,这时,我们就可以使用外观模式来解决这种情况带来的问题。而外观模式的本质是“封装交互,简单调用”。外观模式的结构如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/62664d3d478dd501f9b3eea4533df773.jpeg)
Facade是子系统的多个模块对外的高层接口,接受Facade对象的委派,真正实现功能时,各个模块之间可能会有交互,但有一点要注意的是,Facade对象知道各个模块,但是各个模块不应该知道Facade对象。示例代码如下:
public interface AModuleApi{
public void testA();
}
public class AModuleImple implements AModuleApi{
public void testA(){
System.out.println("AModuleImple");
}
}
public interface BModuleApi{
public void testB();
}
public class BModuleImple implements BModuleApi{
public void testB(){
System.out.println("BModuleImple");
}
}
public interface CModuleApi{
public void testC();
}
public class CModuleImple implements CModuleApi{
public void testC(){
System.out.println("CModuleImple");
}
}
public class Facade{
public void test(){
new AModuleImple().testA();
new BModuleImple().testB();
new CModuleImple().testC();
}
}
public class Client{
public static void main(String[] args){
new Facade().test();
}
}
从上面的代码可以看出,外观模式的目的不是给子系统添加新的功能接口,而是为了让外部减少与子系统内部多个模块的交互,松散耦合,从而让外部能够简单地使用子系统,所以外观应该是用来包装已有的功能,它主要负责组合已有功能来实现客户需要,而不是添加新的实现。在上面的代码中,Facade扮演的角色就是将客户端和系统内部的模块之间的交互屏蔽,封装了系统内部的细节功能。之后如果某个模块发送变化时,只需修改Facade的实现就可以了。
现在我们来总结一下外观模式的优缺点:
外观模式的有如下优点:1.松散耦合,让子系统内部的模块更容易扩展和维护;2.简单易用,客户端不再需要了解子系统内部的实现,也不需要跟子系统内部的模块进行交互,只需要跟外观模式交互即可,很好的体现了“最少知识原则”;3.更好的划分了访问的层次,把需要暴露给外部的功能集中到外观中,这样既方便客户端使用,也很好的隐藏了内部的细节。
外观模式的缺点便是,过多的或者不太合理的Facade也容易让人迷惑,到底是调用Facade好还是直接调用模块好。
那么我们该在何时选用外观模式呢?通常来说,建议在如下情况时选用:1.如果你希望为一个复杂的子系统提供一个简单接口的时候,可以考虑使用外观模式。使用外观对象来实现大部分客户需要的功能,从而简化客户的使用;2.如果想要让客户程序和抽象类的实现部分松散耦合,可以考虑使用外观模式,使用外观对象来将这个子系统与它的客户分离开来,从而提高子系统的独立性和可移植性;3.如果构建多层结构的系统,可以考虑使用外观模式,使用外观对象作为每层的入口,这样可以简化层间调用,也可以松散层次之间的依赖关系。
最后需要注意的是,外观模式同中介者模式需要区别开来。也可以同单例模式、抽象工厂模式组合使用。