这三种设计模式同属于结构型模式,初学者乍一看可能难以分辨三者的区别。这里对三者之间的异同详细地探讨一下。
适配器模式VS装饰模式
首先来比较一下适配器模式和装饰模式。两者都被称为包装模式,在功能上是较为接近的。
适配器模式分为类适配器和对象适配器。类适配器比较直接,继承待适配类,实现目标接口,使得待适配类满足目标接口的特定功能。但是这种直接继承的方法太简单粗暴了,我们知道要尽量多聚合少继承,所以更提倡使用下面的对象适配器。
对象适配器是这次对比的主角,适配器持有一个待适配的对象并且实现了目标接口。通过使用适配器模式,可以大大提升程序的灵活性以及代码的可重复性。
与之相对,装饰者实现待装饰接口或继承待装饰类的同时持有一个待装饰的实例,以此来为待装饰类提供增强的功能。相较于继承来说,装饰模式更为灵活,但使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。
结构上的不同在于从类图中可以看出,装饰模式持有和继承的一般是同一个类或接口,而适配器持有的是待适配的对象,实现的是目标接口。
功能上的不同在于装饰模式中装饰过的类力求与原来对外接口一致。这使得在调用方看来,装饰过后的类与之前没有什么区别,只是某一方面功能增强了。但是适配器模式中适配器则扩展了待适配类新的功能。简单来说,装饰模式增强了原有功能,适配器模式拓展了新的功能。我们也可以看出,装饰模式与适配器模式是不冲突的,可以既增强原有功能,又添加全新的功能。
装饰模式VS代理模式
为什么拿出装饰模式与代理模式再进行对比?首先我们先看一下代理模式的类图。
从类图中可以看代理模式和装饰模式是一致的。这里我要先说明一下,有很多人执着于代理模式的被代理对象是在代理对象的构造函数中new出来的,而不是传进去的参数,并且以此来区分装饰模式和代理模式。我个人认为这是有些偏颇的。比如对于动态代理来说,被代理方肯定要作为参数传入代理方中。
在我看来两者的区别在于:代理模式中,代理方对被代理方有控制权,对于调用方来说两者是分工十分清楚的两个类,调用者很清楚它调用的是代理方。代理方的构造函数可以有多个参数,这使得它可以根据外界环境决定被代理方的运行与否。装饰模式中,装饰者这是增强了被装饰者的某种功能,对于调用方来说两者几乎没有区别。