Java设计模式-外观模式
外观模式又称为门面模式(Facade模式),在23种设计模式中被称为结构型模式。结构型模式分为结构型类模式和结构型对象模式,外观模式属于结构型对象模式。结构型类模式是采用继承机制来组合接口和实现,而结构型对象模式则是描述了如何对一些对象进行组合。在介绍适配器模式时,适配器模式分为类适配器和对象适配器,恰好是典型的两种结构型模式的体现。
外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易使用。
模式分析
- 外观模式可以为子系统中的一组接口提供一个一致的界面,它定义了一个高层接口,这个接口使得这一子系统更加容易使用。
- 外观模式可以将界面与子系统的内部复杂性分隔开,使得界面只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道。
- 外观模式的目的在于降低系统的复杂程度。
- 外观模式从很大程度上提高了客户端使用的便捷性,使得客户端无须关心子系统的工作细节,通过外观角色即可调用相关功能。
适用性
当要为一个复杂的子系统提供一个简单的接口时,子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。外观模式可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过外观层。
客户程序与抽象类或者接口的实现部分之间存在着很大的依赖性。引入外观模式将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。这一点有时容易被忽视,特别是开发一个系统时需要依赖许多第三方库,当第三方库升级后,可能有许多API都变掉了,如果系统中有多处引用了第三方库API有变动的地方,需要更改多处代码且容易出错。但是如果引用第三方库API时,将第三方库统一封装到一个外观类中,这时一旦第三方库有变动,只需要更改一个外观类即可,这样可以有效减少了对第三方库的依赖,降低了系统的耦合性。
结构图
参与者
外观模式的主要参数者如下:
Facade:外观角色
- 知道哪些子系统类负责处理请求。
- 将客户的请求代理给适当的子系统对象。
SubSystem:子系统角色
- 实现子系统的功能。
- 处理由Facade对象指派的任务。
- 没有Facade的任何相关信息;即不包含任何Facade的引用。
示例
典型Facade示例
public class Facade{ private SubSystemA obj1 = new SubSystemA(); private SubSystemB obj2 = new SubSystemB(); private SubSystemC obj3 = new SubSystemC(); public void method(){ obj1.method(); obj2.method(); obj3.method(); } }
电源开关示例
为了使用方便,一个电源总开关可以控制三盏灯、一台空调和一台电视机的启动和关闭。通过该电源总开关可以同时控制上述所有电器设备,使用外观模式设计该系统。
// 电灯子系统 其它子系统代码类似 public class Light { private int index; public Light(int index) { this.index = index; } public void on() { System.out.println(index+"号灯已经打开了"); } public void off() { System.out.println(index+"号灯已经关闭了"); } } // 电源总开关 public class SwitchFacade { private Light[] lights; private Television tv; private AirCondition airCondition; public SwitchFacade() { lights = new Light[3]; for (int i = 0; i < lights.length; i++) { lights[i] = new Light(i + 1); } tv = new Television(); airCondition = new AirCondition(); } public void on() { tv.on(); airCondition.on(); for (int i = 0; i < lights.length; i++) { lights[i].on(); } } public void off() { tv.off(); airCondition.off(); for (int i = 0; i < lights.length; i++) { lights[i].off(); } } } // 客户端 public class Client { public static void main(String[] args) { SwitchFacade switchFacade=new SwitchFacade(); switchFacade.on(); } } // 电视已经打开了 // 空调已经打开了 // 1号灯已经打开了 // 2号灯已经打开了 // 3号灯已经打开了
优缺点
优点
对客户屏蔽了子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易。通过引入外观模式,客户代码将会变得很简单,不需要要考虑子系统代码调用,而是直接使用外观类即可。
实现了子系统与客户代码的松耦合关系,这使得子系统的组件变化不会影响到调用它的客户类,只需要调整外观类即可。
降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程,因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类。
缺点
在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
没有办法直接阻止客户端不通过外观类访问子系统的功能,因为子系统类中的功能必须是公开的(根据需要决定是否使用internal访问级别可解决这个缺点,但外观类需要和子系统类在同一个程序集内)。
其它
外观模式和适配器模式都可以包装许多类,但是外观的意图是简化接口,而适配器的意图是将接口转换为不同的接口。
适配器模式将一个类的接口,转换为客户希望的另一个接口,适配器可以将原本不兼容的类兼容。
外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易使用。
装饰者模式是不改变接口,但是加入责任。装饰模式可以在被装饰者的行为的前面或者后面加上自己的行为,甚至将被装饰者的行为整个替换掉,而达到特定的目的。
参考资料
Head First设计模式
GOF设计模式
Java设计模式