装饰者模式
继承,是面向对象三大基本特性之一。使用继承能够很容易修改和扩展已有的实现,同时很大程度上能够提升代码的可复用性;事实上在面向对象的设计中,通常也是通过继承来实现对给定类的功能扩展。
但是继承是静态的,在编译时便已经决定了子类的行为,往往不便于我们控制扩展的时机和方式,缺少灵活性,同时破坏了类的封装、不易于后期维护。
这个时候,组合的出现便具有了较大的意义。组合即将一个对象嵌入到另一个对象中,由另一个对象来决定什么时机、是否引用该对象来扩展自己的行为。
不同于继承,使用组合并不会破坏类的封装,而且具有较好的松耦合性、利于系统的维护。当然,最重要的是使用组合能够更为灵活的动态控制对原有类的扩展。
装饰者模式就拥有这样一个非常巧妙的结构,它可以动态添加对象功能。如果要扩展功能,装饰者模式提供了更具有弹性的解决方案。
装饰者模式基本结构:
装饰者模式的关键在于装饰者类和真实对象具有相同的接口,并且装饰者类包含一个真实对象的引用,装饰者类将接收到的请求转交给真实对象,并在转交前后添加自己的行为实现。
我们举个例子:
在咖啡店喝咖啡的时候有的人喜欢加牛奶,也有的人喜欢加冰块。
继承就相当于咖啡店一开始就生产出含有牛奶和冰块的咖啡,这固然是可以的。但是有的人喜欢喝纯咖啡,有的人不喜欢喝加了冰块的咖啡,也有的人喜欢喝更甜的咖啡;市场的需求是复杂的,在这种情况下,往往会丢失一大批的客户。
所以咖啡店并不会一开始就生产出含有牛奶和冰块的咖啡,而是在生产出咖啡后同时提供冰块、牛奶,让客户在需要的时候自行添加。
所谓设计模式,最大的功能或者说目的就在于更好的应对变化。
我们再来看下简单的代码实现:
1、首先自然是公用接口
package cn.service;
public interface Compontent {
public void operation();
}
2、喝咖啡方法,也就是我们的真实对象
package cn.service.impl;
import cn.service.Compontent;
public class Coffee implements Compontent{
@Override
public void operation() {
System.out.println("喝咖啡");
}
}
3、因为我们要扩展的功能不止一个(除了加冰还可以加牛奶),所以这里我们使用抽象基类方便共用,实现与真实对象相同的接口
package cn.service.impl;
import cn.service.Compontent;
public abstract class CompontentImpl implements Compontent {
Compontent compontent;
public CompontentImpl( Compontent c) {
compontent = c;
}
}
4、现在到我们的加冰和加牛奶的功能了,因为父类已经实现了接口,所以这里直接继承便可以了
package cn.service.impl;
import cn.service.Compontent;
public class Ice extends CompontentImpl {
public Ice( Compontent c) {
super(c);
}
@Override
public void operation() {
System.out.println("+冰块");
compontent.operation();
}
}
package cn.service.impl;
import cn.service.Compontent;
public class Milk extends CompontentImpl{
public Milk(Compontent c) {
super(c);
}
@Override
public void operation() {
System.out.println("+牛奶");
compontent.operation();
}
}
5、最后我们便可以灵活的添加自己所喜欢的组合了,可以是先加冰再加牛奶,也可以是先加牛奶再加冰,甚至是可以加了牛奶再加牛奶
public static void main( String[] args){
Compontent compontent = new Milk( new Ice( new Coffee()));
compontent.operation();
System.out.println("==========================");
compontent = new Ice( new Milk( new Coffee()));
compontent.operation();
System.out.println("==========================");
compontent = new Milk( new Milk( new Coffee()));
compontent.operation();
}
这个时候大家会发现,对原有对象的扩展变得很灵活了,但是同时也产生了很多小对象。更灵活的特性也就意味着系统的复杂性更高,也同时意味着比继承更容易出错,出错后的排查也更为繁琐。
所以在我们使用装饰者模式的时候需要把握一个度,过度的使用并不可取。
装饰者模式可以让使用者针对接口编程,之后通过不同的方式实现接口类而达到实现不同功能的目标。这种使用模式是否有些似曾相识的感觉?
在Java中,装饰者模式有个很典型的应用:InputStream和OutputStream类族的实现。
(注:部分图片源于网络)