一、
1、使用背景:降低访问复杂系统的内部子系统时的复杂度,简化客户端之间的接口。
2、定义:
为子系统中的一组接口定义一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。完美地体现了依赖倒转原则和迪米特法则的思想。
3、结构图:
4、代码:
// 子系统接口
interface SubsystemA {
void operationA();
}
interface SubsystemB {
void operationB();
}
// 子系统实现
class ConcreteSubsystemA implements SubsystemA {
public void operationA() {
System.out.println("SubsystemA operation");
}
}
class ConcreteSubsystemB implements SubsystemB {
public void operationB() {
System.out.println("SubsystemB operation");
}
}
// 外观类
class Facade
{
private SubsystemA subsystemA;
private SubsystemB subsystemB;
public Facade() {
subsystemA = new ConcreteSubsystemA();
subsystemB = new ConcreteSubsystemB();
}
public void operation() {
subsystemA.operationA();
subsystemB.operationB();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.operation();
}
}
5、优缺点:
(1)优点:
- 减少系统相互依赖。
- 提高灵活性。
- 提高了安全性。
(2)缺点:
不符合开闭原则,如果要改东西很麻烦,继承重写都不合适;
不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和 灵活性;
在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了"开闭原则"。
二、与其他设计模式的联系:
-
外观模式为现有对象定义了一个新接口, 适配器模式则会试图运用已有的接口。 适配器通常只封装一个对象, 外观通常会作用于整个对象子系统上。
-
当只需对客户端代码隐藏子系统创建对象的方式时, 你可以使用抽象工厂模式来代替外观。
-
享元模式展示了如何生成大量的小型对象, 外观则展示了如何用一个对象来代表整个子系统。
-
外观和中介者模式的职责类似: 它们都尝试在大量紧密耦合的类中组织起合作。
- 外观为子系统中的所有对象定义了一个简单接口, 但是它不提供任何新功能。 子系统本身不会意识到外观的存在。 子系统中的对象可以直接进行交流。
- 中介者将系统中组件的沟通行为中心化。 各组件只知道中介者对象, 无法直接相互交流。
-
外观类通常可以转换为单例模式类, 因为在大部分情况下一个外观对象就足够了。
-
外观与代理模式的相似之处在于它们都缓存了一个复杂实体并自行对其进行初始化。 代理与其服务对象遵循同一接口, 使得自己和服务对象可以互换, 在这一点上它与外观不同。
三、使用场景:对于复杂难以维护的老系统,可以为新系统开发一个外观 Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。实现方法:
- 考虑能否在现有子系统的基础上提供一个更简单的接口。 如果该接口能让客户端代码独立于众多子系统类, 那么你的方向就是正确的。
- 在一个新的外观类中声明并实现该接口。 外观应将客户端代码的调用重定向到子系统中的相应对象处。 如果客户端代码没有对子系统进行初始化, 也没有对其后续生命周期进行管理, 那么外观必须完成此类工作。
- 如果要充分发挥这一模式的优势, 你必须确保所有客户端代码仅通过外观来与子系统进行交互。 此后客户端代码将不会受到任何由子系统代码修改而造成的影响, 比如子系统升级后, 你只需修改外观中的代码即可。
- 如果外观变得过于臃肿, 你可以考虑将其部分行为抽取为一个新的专用外观类。