目录
外观模式
引言
一般情况下,一个Web网站都会提供一个首页,作为网站的入口,我们只需要记住这个首页的地址,即可访问它的各个子页面的超链接。当然,用户也可以记住每个子页面的地址,直接通过子页面的地址直接访问。实际上用户是不会记住每一个子页面的地址,此时可以通过首页间接的访问期望访问的子页面,在这里首页就扮演了一个“外观角色”。
定义
英文定义:"Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use."。
中文定义:外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层次接口,这个接口使得这一子系统更加容易使用。
外观模式重要等级★★★★★ 外观模式难度等级★☆☆☆☆
模式类图
外观模式包含俩个角色:
1.Facade(外观角色)
2.SubSystem(子系统角色)
实例
实例描述
自己泡咖啡需要自己准备开水(Boil Water)、准备咖啡(Prepare Coffee)、准备杯子(Prepare Cup)、还要自己去煮(Cook Coffee),而去咖啡厅喝咖啡只需要把要求告诉服务员,所有的过程由服务员来完成,此时服务员就是外观角色。用外观模式实现该场景,并且在测试代码中可以体现外观模式的有点。
实例类图
代码实现
1.Water 水类,提供烧水方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
class Water
{
public void Boil()
{
Console.WriteLine("烧开水");
}
}
2.Coffee 咖啡类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
class Coffee
{
private string brand;
public Coffee(string brand)
{
this.brand = brand;
}
public void PrepareCoffee()
{
Console.WriteLine("准备{0}咖啡原料",brand);
}
}
3.Cup 杯子类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
class Cup
{
public enum CupType
{
CoffeeCup,
VacuumCup,
masturbationCup
}
private CupType type;
public Cup(CupType type)
{
this.type = type;
}
public void PrepareCup()
{
Console.WriteLine("准备杯子:" + type);
}
}
4.Cooker 厨师类,提供煮咖啡方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
class Cooker
{
public void CookCoffee(Coffee coffee,Cup cup,Water water)
{
Console.WriteLine("煮咖啡:");
water.Boil();
coffee.PrepareCoffee();
cup.PrepareCup();
}
}
5.Waiter 服务员类,提供点咖啡方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
class Waiter
{
public void Order(string coffeeName)
{
Cooker cooker = new Cooker();
Water water = new Water();
Cup cup = new Cup(Cup.CupType.CoffeeCup);
Coffee coffee = new Coffee(coffeeName);
cooker.CookCoffee(coffee, cup, water);
}
}
6.不使用外观模式的测试代码(不使用Waiter外观类)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
Cooker cooker = new Cooker();
Water water = new Water();
Cup cup = new Cup(Cup.CupType.CoffeeCup);
Coffee coffee = new Coffee("摩卡");
cooker.CookCoffee(coffee, cup, water);
Console.ReadKey();
}
}
7.运行结果
8.使用外观模式的测试代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
Waiter waiter = new Waiter();
waiter.Order("摩卡");
Console.ReadKey();
}
}
9.运行结果
对比俩种测试代码,可以明显发现,它们都达到了相同的效果,但是使用外观类降低了客户端与子系统之间的耦合,客户端调用时无需再关心各个类如何使用,大大地简化了客户端代码。
模式扩展
外观类的数量
在外观模式中,通常只需要一个外观类,并且次外观类只有一个实例,换而言之他是一个单例类。在很多情况下为了节约系统资源,一般将外观类设计为单例类。当然,整个系统中也可以存在多个外观类,每个外观类都与一些特定的子系统交互,向用户提供相应的业务功能。
抽象外观类
外观模式在增加新的子系统或者移除子系统时需要修改外观类,违背了“开闭原则”。引入抽象外观类在一定程度上可以解决该问题:面对新的业务需求,不修改原有的外观类,而是通过新增一个具体外观类,客户端针对抽象编程,这样只需要修改配置文件或者反射即可更换新的外观类。
总结
模式优点
1.通过引入外观模式,外观类,客户端代码只和外观类直接通信,代码变得简单。
2.实现了子系统与客户之间的松耦合关系。
3.客户端依然可以直接访问子系统。
模式缺点
1.客户端依然可以直接访问子系统。(这既是优点有是缺点,如果客户端频繁直接访问子系统,那么会增加它们之间的耦合度)
2.在不引入抽象外观类的情况下,新增的需求可能需要修改外观类(违背了开闭原则)。