Java与模式-外观模式

来源:互联网 发布:淘宝小卖家还能做吗 编辑:程序博客网 时间:2024/05/18 20:11
在当下的中国医疗环境下,病人去医院看病,一般都需要自己去挂号,然后去门诊,医生诊断完成之后,去划价,然后取药。这个流程是医院强加给病人的,病人为了看病就只能屈从于医院的这种安排,病人必须掌握这种流程,必须去一一做这些事情,想当一个合格的病人或者病人家属还挺不容易的。如果把看病作为一个子系统,挂号,门诊,划价,取药就是这个子系统中的具体模块,我们给出一个示意性代码:
/* 挂号的示意性代码 */class Register {public void doRigister() {System.out.println("挂号成功,请去门诊候诊!");}}/*诊断的示意性代码*/class Diagnose {public void doDiagnose() {System.out.println("诊断完毕,请去划价!");}}/*划价的示意性代码*/class Charge {public void doCharge() {System.out.println("成功划价!");}}/*取药的示意性代码*/class Medicines {public void doMedicines() {System.out.println("红色药丸一天三次,一次三粒");}}/*办理住院手续的示意性代码*/ class BeInHospital {public void doIn() {System.out.println("办理主元素后续");}}/*使用CT检查的示意代码*/class CT {public void doCT() {System.out.println("CT预约成功!");}}
病人对应着客户端:
/*在当前环境下,病人看病的流程*/class Patient {public static void main(String[] args) {System.out.println("---来到医院,茫然无措,不知道该做什么,很是恐慌---");//挂号new Register().doRigister();//门诊new Diagnose().doDiagnose();//划价new Charge().doCharge();//取药new Medicines().doMedicines();}}

我们可以看到,为了完成看病的流程,客户端不得不去跟系统中的一个个模块去打交道,客户端必须得掌握各个模块,同时又很模块耦合的很紧密,一旦某个模块发生变化,客户端也要相应的改变。这可不是好的设计思路。用图来表示上面的例子会更直观一些

如果医院能派一个人来专门负责这些事情,或者提供一个绿色通道,只在一个地方办一次手续就完成了大部分的工作,如果是这样的话,那这个病人无疑会稍稍幸福一些。比如现在看病的流程是这样:


如果把上面的情形放到软件系统中就得到一个设计模式。

外观模式

有几个词需要说明一下,外观,视图,接口,当我们说外观、视图时不是说图形界面,而是说从外面看一个模块或者类时,我们能看到的内容,也许上面的话来形容接口更恰当,这里的接口不是指Java中的interface,而是Java中的类(或者接口)中暴露于外的内容,比如public的内容。还以上面的例子来说明,看病的具体流程就是系统中的一个模块,这些模块组合在一起就是一个子系统,外观定义了子系统的一个接口,也就是客户端通过外观能看到的内容。下面给出外观模式的定义:外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

在没有使用外观模式的看病流程中有两个缺点:客户端必须要掌握很多模块;客户端和各个模块耦合紧密。使用了外观模式之后,客户端只要通过外观模式就能得到想要的内容,简化了学习难度,没有必要去掌握各个模块的细节。同时,外观还使客户端和各个模块解耦,在以后如果模块发生了变化,这些变化会被外观接口消化,客户端不会感知这种变化。外观模式的优点:提供统一的接口,简化访问;将客户与子系统解耦。这其实也是外观模式的本质:封装交互,简化调用。

使用外观模式来重写上面的例子,各个模块无需重写,我们需要增加一个外观类:

/*外观类,给客户端提供了一个视图,它应该是个单例类*/public class Facade {private static class Holder {private static Facade instance = new Facade();}public static Facade getInstance() {return Holder.instance;}/*缺省实现,对大多数人来说足够*/public void doDefault() {//挂号new Register().doRigister();//门诊new Diagnose().doDiagnose();//划价new Charge().doCharge();//取药new Medicines().doMedicines();}public void doAll(boolean isCT) {//挂号new Register().doRigister();//门诊new Diagnose().doDiagnose();if(isCT) {new CT().doCT();}//划价new Charge().doCharge();//取药new Medicines().doMedicines();}}
这个时候客户端在去调用就会很简单了:
class Patient {public static void main(String[] args) {System.out.println("---使用了外观模式之后---");Facade facade = Facade.getInstance();facade.doDefault();//客户端还是可以直接使用各个模块Medicines m = new Medicines();m.doMedicines();}}
说明

外观接口没有给子系统增加任何新的功能,外观的目的是提供一个高层的接口,方便客户端调用,同时将模块与客户端解耦。对于客户端的请求,外观接口还是调用具体的模块去执行,它相当于一个代理。对于客户端而言,外观接口是子系统的一个视图,但是对于具体的模块而言,外观接口相当于客户端。也就是说,外观接口需要知道各个模块的存在,要掌握的它们的细节,但是具体的模块并不知道外观接口的存在。从语法上讲,可以在外观接口上增加一些额外的功能,但是这并不是外观接口的本意。

客户端可以绕过外观接口去访问具体的模块,以实现特别的功能。外观封装了一个缺省的实现,这个实现对于大多数人来说已经足够了,同时又简化了学习难度,只要知道外观接口就可以正确的使用子系统。但是对于有特别需求的客户而言,他们完全可以调用具体的模块进行更精确的控制。

可以为子系统实现一个以上的外观,但是过多的外观会引起混乱,客户端不知道到底该调用哪个外观,或者是直接调用具体模块。

最少知识原则(迪米特原则)

最少知识原则:只和你的密友谈话。它告诉我们要减少对象之间的交互,只留下几个“密友”。也就是说,当你在设计一个系统时,不管是任何对象 ,你都要注意它所交互的类有哪些,并注意它和这些类是怎么交互的。这个原则希望我们在设计中,不要让太多的类耦合到一起,免得修改系统中的一部分,会影响其他部分。就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法:

(i)   该对象本身的方法

(ii)  被当做方法的参数传递进来的对象的方法

(iii) 该对象持有对象的方法

(iiii)此方法创建的对象的方法

使用最少知识原则可以减少对象之间的依赖,但是会导致更多的包装类被引入进来。

在外观模式中就遵循了最少知识原则,客户端只有外观接口这一个朋友,成功的与具体模块解耦,这样子系统可以在不影响客户端的情况下进行修改,这些改变都会被外观模式所消化。

使用场景

1)为一个复杂的子系统提供一个简单的接口

2)提供子系统的独立性,将子系统与其他的部分解耦

3)在层次系统中,可以使用外观模式定义每一层的入口

4)在维护旧系统时,如果有新的功能依赖原来的模块,这个时候可以将外观模式应用到旧系统中,新的功能只从外观接口中获得功能,对旧系统的维护和修改不会影响新功能的调用。

外观模式提供了一个高层的接口,它帮助客户容易的调用子系统,同时将客户端与子系统解耦,它不会增加新的功能,客户端也可以绕过它去访问底层的模块,因为外观模式的存在,客户变得简单而又富有弹性。

转载请注明出处:喻红叶《Java与模式-外观模式》


0 0
原创粉丝点击