概述:
在软件开发中我们常常遇到这样的问题,在一个系统中有多个子系统,而一个应用型的类(客户类)需要和各个子系统打交道来完成一件事情,这样客户代码就会和子系统类耦合。在生活中我们也会遇到这样的问题,打个比方:买房子。我在买房子的时候首先是选,到一个房产开发商那里有售楼小姐给你做介绍,你选中好房子后要办理贷款,签合同,办贷款的时候还要咨询贷款额度,让公司开收入证明,还款类型,指定贷款年。然后还要到售楼处去办理定金,一星期内还要交首付。在这个过程中我要和售楼小姐、我所在的公司、银行,售楼出出纳员,售楼经理好多人打交道,大家试想下如果这个就是一个系统的业务流程那么作为我(用户)需要和这个系统中的子系统多次打交道,这样就形成了客户程序与复杂的系统内部子系统间的紧耦合,从而导致客户程序随着子系统变化而变化。那如何简化客户程序和子系统间的交互呢,这里我们需要再添加一个买房统一接口类(SellMan),帮我做买房的事情这样客户程序(我)不再依赖系统中的子系统。这就是外观模式。看下图:
<shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"></path><lock v:ext="edit" aspectratio="t"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 414.75pt; HEIGHT: 296.25pt" type="#_x0000_t75"><imagedata src="file:///C:%5CDOCUME~1%5CFANWEI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.emz" o:title=""></imagedata></shape>
解释:
1. 定义一个IBack接口创建PutOUt 放款方法
2. 定义一个IAccountCredit接口创建HasGoodCredit 判断是否可以贷款方法
3. 定义一个Back银行的抽象类,继承IBack,IAccountCredit接口
4. 定义工商(BusinessBack)、农业(AgroBack)、建设(ConstructBack)、交通(TranfficBack)四大银行继承自Back
5. 定义一个ISell接口创建Sell 卖房方法
6. 创建LandAgent房产商继承ISell接口
7. 创建House房屋类
8. 定义一个IBuyHouse接口创建Buy 买房方法
9. 创建SellMan买房屋代理人
10. 创建Client买房人
这样一来客户程序和子系统的通信有中间的SellMan统一协调,通过SellMan的Buy接口做买卖,这样就实现了解耦合。来看看他的客户端的代码把,如此简单:
Client client = new Client();
client.Name = "范伟伟";
client.CreditCount = 500000;
SellMan sell = new SellMan(client);
House house=sell.Buy(100000);
if (house != null)
Console.WriteLine(string.Format("{0}将{1}房屋卖给了{2}", sell.LandAgent.Name, house.ID, client.Name));
else
Console.WriteLine("交易失败");
效果:
OOD设计合理性:
1. 是否符合开不原则:
客户程序和子系统的通信有中间的SellMan统一协调,通过SellMan的Buy接口做买卖,再做修改时只要修改SellMan Buy 方法或再创建一个继承了Buy接口的类
符合
2. 是否符合里氏代换:
符合
3. 是否符合抽象原则
符合
4. 是否符合迪米特法则
符合
装饰模式总结
意图:
为子系统中的一组接口提供一个一致的界面,Façade模式定义了一个高层接口,这个接口使得子系统更加容易操作
结构图:
<shape id="_x0000_i1026" style="WIDTH: 191.25pt; HEIGHT: 204.75pt" type="#_x0000_t75" alt=""><imagedata src="file:///C:%5CDOCUME~1%5CFANWEI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image003.gif" o:href="资料/设计模式(15)-Facade%20Pattern%20-%20First%20we%20try,%20then%20we%20trust%20-%20博客园_files/Pic90.gif"></imagedata></shape>
门面(Facade)角色:客户端可以调用这个角色的方法。此角色知晓相关的(一个或者多个)子系统的功能和责任。在正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去。
子系统(subsystem)角色:可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。每一个子系统都可以被客户端直接调用,或者被门面角色调用。子系统并不知道门面的存在,对于子系统而言,门面仅仅是另外一个客户端而已。
使用背静:
1. 为一个复杂系统提供一个简单的统一的接口
2. 客户程序与抽象类的实现部分之间存在着很大的依赖性
3. 当需要构建一个层次结构的子系统时,使用外观模式定义子系统中每层的入口点。
模式优缺点:
优点:
1. 他对客户屏蔽了系统组件,因此减少了客户处理的对象的数目并使得子系统使用起来更加方便
2. 使得子系统与客户程序间的松耦合
3. 他并不限制他们使用子系统类,你可以在系统易用性和通用性之间加以斟酌。
注意:
1. 降低客户-子系统之间的耦合度。
用抽象类实现Façade而他的具体子类对应于不同的子系统实现,这可以进一步降低客户与子系统的耦合度。(这里SellMan我没有用抽象类是因为我只为买卖这一层做Facade)。这样,客户就可以通过抽象的Façade类接口与子系统通讯。这种抽象耦合关系使得客户不知道它使用的是子系统的哪一个实现
除了生成子类的方法外,另一种方法是用不同的子系统对象配置Façade对象。为定制façade,近需对他的子系统(一个或多个)进行替换即可。
2. 公共子系统类与私有子系统类
C#不支持私有接口的实现所以这里不多讲
源代码 下载