目录
1.概览
1.1 设计宗旨
1.2 解决问题
1.3 实现办法
1.4 UML类图
1.5 举例说明(非原创)
2. 代码举例
3. 小节
隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。
1.降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。
通过 【外观】这个中介,把 “客户端” 和 “复杂系统” 分离,【外观】将调用顺序、依赖关系等处理好 。
SystemA:定义子系统A的功能。
SystemB:定义子系统B的功能。
Facade:定义客户端访问的功能 & 处理子系统之间的依赖或者关联。
现代的软件系统都非常复杂,尽管我们已经想尽一切方法将其“分而治之”,把一个系统划分为好几个较小的子系统了,但是仍然可能会存在这样的问题:子系统内有非常多的类,客户端往往需要和许多对象打交道之后 才能完成想要完成的功能。
在我们的生活中医院就是这样的。一般的医院都会分为挂号、门诊、化验、收费、取药等。看病的病人要想治好自己的病(相当于一个客户端想要实现自己的功能)就要和医院的各个部门打交道。首先,病人需要挂号,然后门诊,如果医生要求化验的话,病人就要去化验,然后再回到门诊室,最后拿药,经过一系列复杂的过程后才能完成看病的过程。如下图所示:
解决这种不便的方式就是引入门面模式。如果我们在医院设立一个接待员的话,病人只负责和接待员接触,由接待员负责与医院的各个部门打交道,如下图所示:
这里以造车为例,假设每辆车都有一下属性:
public class Car
{
public string Type { get; set; } = string.Empty;
public string Color { get; set; } = string.Empty;
public int NumberOfDoors { get; set; }
public string City { get; set; } = string.Empty;
public string Address { get; set; } = string.Empty;
public override string ToString()
{
return $"CarType: {Type}, Color: {Color}, Number of doors: {NumberOfDoors}, Manufactured in {City}, at address: {Address}";
}
}
我们定义一个外观模式建造者——也就是制作专门类型的车:
public class CarBuilderFacade
{
protected Car Car { get; set; }
public CarBuilderFacade()
{
Car = new Car();
}
public Car Build() => Car;
}
在这个类中产生Car实例,并在Build函数暴露Car对象。接下来我们定义Car的外观,我们将外观定义放在两类中,一类定义颜色,品牌,门数,一类定义生产城市地址。
public class CarInfoBuilder:CarBuilderFacade
{
public CarInfoBuilder(Car car)
{
Car = car;
}
public CarInfoBuilder WithType(string type)
{
Car.Type = type;
return this;
}
public CarInfoBuilder WithColor(string color)
{
Car.Color = color;
return this;
}
public CarInfoBuilder WithNumberOfDoors(int number)
{
Car.NumberOfDoors = number;
return this;
}
}
public class CarAddressBuilder:CarBuilderFacade
{
public CarAddressBuilder(Car car)
{
Car = car;
}
public CarAddressBuilder InCity(string city)
{
Car.City = city;
return this;
}
public CarAddressBuilder AtAddress(string address)
{
Car.Address = address;
return this;
}
}
类定义很简单,只要注意两个类都是派生于CarBuilderFacade类,且在函数中返回自身,这样做的目的就是为了实现“Fluent”调用。
此时我们还要回到原来的建造类中添加上面两个类的实例:
public class CarBuilderFacade
{
protected Car Car { get; set; }
public CarBuilderFacade()
{
Car = new Car();
}
public Car Build() => Car;
public CarInfoBuilder Info => new CarInfoBuilder(Car);
public CarAddressBuilder Built => new CarAddressBuilder(Car);
}
添加了Info和Built两个实例,就可以定制化外观类,且因为这两个实例派生与CarBuilderFacade,
所以在调用时,可一直接一call到底。
// See https://aka.ms/new-console-template for more information
using FacedBuilder;
var car = new CarBuilderFacade()
.Info
.WithType("BMW")
.WithColor("Red")
.WithNumberOfDoors(5)
.Built
.InCity("上海")
.AtAddress("南京路")
.Build();
Console.WriteLine(car);
这样看起来是不是有一种优雅的感觉?
在外观模式中,通常只需要一个外观类,并且此外观类只有一个实例,换言之它是一个单例类。在很多情况下为了节约系统资源,一般将外观类设计为单例类。当然这并不意味着在整个系统里只能有一个外观类,在一个系统中可以设计多个外观类,每个外观类都负责和一些特定的子系统交互,向用户提供相应的业务功能。 这个很好理解,我们的CarBuilderFacade就是用来建造不同的Car的。
不要通过继承一个外观类在子系统中加入新的行为,这种做法是错误的。外观模式的用意是为子系统提供一个集中化和简化的沟通渠道,而不是向子系统加入新的行为,新的行为的增加应该通过修改原有子系统类或增加新的子系统类来实现,不能通过外观类来实现。
外观模式创造出一个外观对象,将客户端所涉及的属于一个子系统的协作伙伴的数量减到最少,使得客户端与子系统内部的对象的相互作用被外观对象所取代。外观类充当了客户类与子系统类之间的“第三者”,降低了客户类与子系统类之间的耦合度,外观模式就是实现代码重构以便达到“迪米特法则”要求的一个强有力的武器。