外观模式
外观模式
定义(来自维基百科)
外观模式(Facade pattern)<[fə'sɑd] ['pætɚn]>又名“门面模式”,其为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用。
图示
应用外观模式之前
应用外观模式之后
解读
- 外观模式也是得墨忒耳定律(Law of Demeter)/迪米特法则的实现,根据单一职责原则,在软件中将一个系统划分为若干个子系统有利于降低整个系统的复杂性,一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小,而达到该目标的途径之一就是引入“外观”对象,它为子系统的访问提供一个简单的入口。
- 通过定义一个高层的统一的类,包装子系统中一个或多个复杂的类。从而使消费者/客户端可以直接通过这个统一的类来调用子系统中的方法,也使得消费者/客户端和子系统之间避免了紧耦合。
- 换言之,通过封装了客户端和子系统之间的交互,从而降低了消费者/客户端和子系统之间的沟通成本。
- 以上“子系统”指被“外观”的一个或多个类,当然,这些被“外观”的类,应当是某功能或某业务领域的类的聚合。
- 外观模式可以有选择的暴露/封装子系统中的方法
类型
适用场景
- 当要为一个复杂的子系统提供一个简单的接口时可以使用外观模式。该接口可以满足大多数用户的需求,而且用户也可以越过外观类直接访问子系统;
- 客户程序与子系统之间存在很大的依赖性,引入外观类将子系统与客户端/消费者进行解耦,可以提高子系统的独立性和可移植性;
- 在层次化结构中,可以使用外观模式定义每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。
C#示例
以微信公众号开发为例, 微信公开了公众号开发的很多Api,那么我们在工作的过程中,虽然允许消费者/客户端去直接调用其网络接口,但遍布业务系统的微信Api调用势必会为系统的开发和维护增加成本,通过应用外观模式,将微信的Api“包”起来,使得客户端/消费者只需通过一个统一的入口即可方便的使用微信的各个Api,势必可以大大提升开发效率。
using System;
using System.Net.Http;
using System.Threading.Tasks;
/* 某业务子系统 */
public class WXApi
{
public static string GET_USERS = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENDI";
public static string GET_CUSTOM_MENUS = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";
}
public class WeiXinNetApi
{
// 获取用户列表
public async List<object> GetUsers()
{
var url = WXApi.GET_USERS;
using (HttpClient client = new HttpClient())
{
var result = await client.GetAsync(url);
return result;
}
}
// 获取用户自定义菜单
public async List<object> GetCustomMenus()
{
var url = WXApi.GET_CUSTOM_MENUS;
using (HttpClient client = new HttpClient())
{
var result = await client.GetAsync(url);
return result;
}
}
// ...
}
// 消费者
public class Program
{
public static void Main()
{
var wx = new WeiXinNetApi();
var users = wx.GetUsers();
var menus = wx.GetCustomMenus();
Console.WriteLine(users);
Console.WriteLine(menus);
Console.ReadKey();
}
}
优点
- 减少了客户端/消费者处理的对象数目并使得子系统使用起来更加容易;
- 实现了客户端/消费者与子系统之间的松耦合关系,子系统的变化不会影响到调用它的客户端,只需要调整它的外观类即可;
- 降低了软件系统中的编译依赖,简化系统移植过程,一个子系统的修改不会影响其他子系统;
- 提供访问子系统的统一入口,但不影响用户直接消费子系统类。
缺点
- 不能很好的限制客户端使用子系统的类,如果对客户端/消费者做太多的限制则减少了可变性和灵活性;
- 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。