一、简介
1.1 引入
在寒假结束以后,开学第一天,数学老师进门说:“你们把寒假作业交上来”,每个人独自来我办公室交作业。于是,同学陆续前往办公室交作业,一下子,办公室被围得水泄不通。
语文老师听说了数学老师的"事迹",让语文课代表把所有同学的寒假作业收齐以后,统一送到语文老师的办公室。
我们能够看到,语文老师办法显然更加方便。如果有哪位同学没有交齐作业,只需要让课代表记录一下,这样就可以减轻老师的工作量了。
语文老师的办法和接下来我们要了解的外观模式很类似,接下来就让我们一起去了解一下什么是外观模式吧。
1.2 定义
外观(Facade)模式:外观模式又叫门面模式,它为多个复杂的子系统提供一个一致的接口,这个接口使得这些子系统更容易被使用
- 该模式对外有一个"统一的接口",外界可以通过这个接口访问子系统,而不用直接访问子系统
- 外部应用程序在访问子系统时,不用关心内部子系统的具体细节,大大降低了应用程序的复杂度,提高了程序的可维护性。
二、模式原理
2.1 模式组成
组成(角色) | 作用 |
---|---|
Facade(外观) | Facade是一个类,该类包含了子系统中全部或部分类的实例引用。当用户要和子系统打交道时,可以通过和Facade外观类来和子系统打交道 |
Subsystem(子系统) | 子系统可以有一个或多个,每个子系统可以不是一个单独的类,而是若干个类的集合,这些类的实例协同合作为用户提供所需要的功能,子系统并不知道外观类的存在,所以子系统中的任何一个类都不包含对外观类的实例引用。 |
2.2 UML类图
三、实例
3.1 实例概况
- 概括:数学老师学习了语文老师的办法,让语文课代表收集班级同学的寒假作业(这里假设班级人数3人)
- 利用外观模式帮助数学老师收集作业,并输出每位同学作业完成情况
3.2 步骤
- 步骤一:创建外观类,并且包含所有子系统的实例引用,可自己组合相关的方法
//外观类,包含了所有子系统的实例引用
public class MathRep {
public LiHua liHua;
public Tom tom;
public Jerry jerry;
// 因为子系统中的getInstance()都是静态的,所以可以直接用类名调用
public MathRep() {
liHua = LiHua.getInstance();
tom = Tom.getInstance();
jerry = Jerry.getInstance();
}
// 提交作业,调用所有子系统的实例,用户只需要直接调用 外观角色 即可,无需自己创建子系统实例
public void submit() {
liHua.submit();
tom.submit();
jerry.submit();
}
public void condition() {
liHua.condition();
tom.condition();
jerry.condition();
}
}
- 步骤二:创建子系统,各个子系统互相独立,使用单例模式-饿汉式创建实例
//子系统,聚合到外观类中
public class LiHua {
// 单例模式,饿汉式
private static LiHua liHua = new LiHua();
public static LiHua getInstance() {
return liHua;
}
// 作业提交
public void submit() {
System.out.println("李华提交数学作业");
}
// 作业完成情况
public void condition() {
System.out.println("李华完成情况100%,well done");
}
}
//子系统,聚合到外观类中
public class Tom {
// 单例模式,饿汉式
private static Tom tom = new Tom();
public static Tom getInstance() {
return tom;
}
// 作业提交
public void submit() {
System.out.println("汤姆提交数学作业");
}
// 作业完成情况
public void condition() {
System.out.println("汤姆完成情况80%,Do ok");
}
}
//子系统,聚合到外观类中
public class Jerry {
// 单例模式,饿汉式
private static Jerry jerry = new Jerry();
public static Jerry getInstance() {
return jerry;
}
// 作业提交
public void submit() {
System.out.println("杰瑞提交数学作业");
}
// 作业完成情况
public void condition() {
System.out.println("杰瑞完成情况59%,don't pass");
}
}
- 步骤三:用户通过外观类实现相关功能,不用和子系统直接接触
public class MathTeacher {
public static void main(String[] args) {
// 用户不需要和子系统打交道,只需要直接和外观类打交道即可
// 创建一个数学课代表,由课代表收作业,反映作业完成情况
MathRep mathrep = new MathRep();
mathrep.submit();
System.out.println("----------");
mathrep.condition();
}
}
//执行结果
李华提交数学作业
汤姆提交数学作业
杰瑞提交数学作业
----------
李华完成情况100%,well done
汤姆完成情况80%,Do ok
杰瑞完成情况59%,don't pass
3.3 UML类图
四、优缺点
4.1 优点
- 使客户端和子系统中的类无耦合,使得子系统使用起来更加方便
- 一个子系统的修改对其他子系统没有任何影响,而且子系统中任何类对其方法进行修改,并不影响外观类的代码
- 只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类
- 降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程,因为编译一个子系统一般不需要编译所有其他的子系统
4.2 缺点
- 不能很好地限制客户直接使用子系统类,如果对客户访问子系统类做太多的限制,就减少了可变性和灵活性
- 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了"开闭原则"
五、应用场景
- 对于一个复杂的子系统,需要为用户提供一个简单接口时。并且该接口可以满足大多数用户的需求,而且用户也可以越过外观类直接访问子系统
- 不希望客户端和子系统之间存在耦合,可以使用外观模式让客户端和子系统解耦,以便提高子系统的独立性和可移植性
- 当整个系统需要构建一个层次结构的子系统,不希望这些子系统相互直接的交互时