步步为营 .NET 设计模式学习笔记 十六、Facade(外观模式)
概述
在软件开发系统中,客户程序经常会与复杂系统的内部子系统之间产生耦合,而导致客户程序随着子系统的变化而变化。那么如何简化客户程序与子系统之间的交互接口?如何将复杂系统的内部子系统与客户程序之间的依赖解耦?这就是要说的Façade 模式。重新进行类的设计,将原来分散在源码中的类/结构及方法重新组合,形成新的、统一的接口,供上层应用使用。 Facade所面对的往往是多个类或其它程序单元,通过重新组合各类及程序单元,对外提供统一的接口/界面。
意图
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。[GOF 《设计模式》]
示意图
门面模式没有一个一般化的类图描述,下面是一个示意性的对象图:
图1 Façade模式示意性对象图
在这个对象图中,出现了两个角色:
门面(Facade)角色:客户端可以调用这个角色的方法。此角色知晓相关的(一个或者多个)子系统的功能和责任。在正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去。
子系统(subsystem)角色:可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。每一个子系统都可以被客户端直接调用,或者被门面角色调用。子系统并不知道门面的存在,对于子系统而言,门面仅仅是另外一个客户端而已。
生活中的例子
外观模式为子系统中的接口定义了一个统一的更高层次的界面,以便于使用。当消费者按照目录采购时,则体现了一个外观模式。消费者拨打一个号码与客服代表联系,客服代表则扮演了这个"外观",他包含了与订货部、收银部和送货部的接口。
图2使用电话订货例子的外观模式对象图
示例用例图
在公司上班,每个月公司在月初都会给员工结算上一个月的薪水,首先检查输入的工号是不是公司内在职员工,检查员工的出勤率,都没有问题,再结算员工薪水,用例图如下:
代码设计
先创建Employee.cs:
public class Employee { public bool IsCompanyEmployee(string userID) { return true; } }
再创建Attendance.cs:
public class Attendance { public bool CheckAttendance(string userID) { return true; } }
再创建Salary.cs:
public class Salary { public decimal PayEmployeeSalary(string userID) { return 10000; } }
再创建SalarySystem.cs:
public class SalarySystem { public SalarySystem() { } public decimal GetEmplayeeSalary(string userID) { decimal employeeSalary = 0; Employee employee = new Employee(); Attendance attendance = new Attendance(); Salary salary = new Salary(); if (!employee.IsCompanyEmployee(userID)) { return employeeSalary; } if (!attendance.CheckAttendance(userID)) { return employeeSalary; } employeeSalary = salary.PayEmployeeSalary(userID); return employeeSalary; } }
最后再调用:
public partial class Run : Form { public Run() { InitializeComponent(); } private void btnRun_Click(object sender, EventArgs e) { //------------------------------------- SalarySystem salarySystem = new SalarySystem(); string userID = "021901"; rtbResult.AppendText(string.Format("{1}号员工的薪水是{0}.",salarySystem.GetEmplayeeSalary(userID),userID)); } }
运行结果如下图:
效果及实现要点
1.Façade模式对客户屏蔽了子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便。
2.Façade模式实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的。松耦合关系使得子系统的组件变化不会影响到它的客户。
3.如果应用需要,它并不限制它们使用子系统类。因此你可以在系统易用性与通用性之间选择。
4.通过一个高层接口让子系统和客户端不发生直接关联,使客户端不受子系统变化的影响。
5.Facade不仅仅针对代码级别,在构架上,特别是WEB应用程序的构架上,Facade的应用非常普遍。
适用性
1.为一个复杂子系统提供一个简单接口。
2.提高子系统的独立性。
3.在层次化结构中,可以使用Facade模式定义系统中每一层的入口。
4.从代码角度来说, 如果你的程序有多个类是和一组其它接口发生关联的话可以考虑在其中加一个门面类型。
5.从应用角度来说, 如果子系统的接口是非常细的,调用方也有大量的逻辑来和这些接口发生关系,那么就可以考虑使用Facade把客户端与子系统的直接耦合关系进行化解。你可能会说,子系统改了门面不是照样改?的确是需要改,但是如果客户端本身的工作已经比较复杂,或者说可能有多个需要调用门面的地方,这个时候门面的好处就体现了。
优点
1.它对客户屏蔽子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便。
2.它实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的。
松耦合关系使得子系统的组件变化不会影响到它的客户。Facade模式有助于建立层次结构系统,也有助于对对象之间的依赖关系分层。Facade模式可以消除复杂的循环依赖关系。这一点在客户程序与子系统是分别实现的时候尤为重要。
在大型软件系统中降低编译依赖性至关重要。在子系统类改变时,希望尽量减少重编译工作以节省时间。用Facade可以降低编译依赖性,限制重要系统中较小的变化所需的重编译工作。Facade模式同样也有利于简化系统在不同平台之间的移植过程,因为编译一个子系统一般不需要编译所有其他的子系统。
3.如果应用需要,它并不限制它们使用子系统类。因此你可以在系统易用性和通用性之间加以选择。
总结
Façade模式注重的是简化接口,它更多的时候是从架构的层次去看整个系统,而并非单个类的层次。