Java与模式-外观模式

在当下的中国医疗环境下,病人去医院看病,一般都需要自己去挂号,然后去门诊,医生诊断完成之后,去划价,然后取药。这个流程是医院强加给病人的,病人为了看病就只能屈从于医院的这种安排,病人必须掌握这种流程,必须去一一做这些事情,想当一个合格的病人或者病人家属还挺不容易的。如果把看病作为一个子系统,挂号,门诊,划价,取药就是这个子系统中的具体模块,我们给出一个示意性代码:
/* 挂号的示意性代码 */
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与模式-外观模式》


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值