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设计模式

评论

您确定要删除吗?删除之后不可恢复