引言
IO系统是使用了装饰器模式的典型。所以对装饰器模式的深入研究对IO系统的理解肯定大有裨益。在本文中会详细介绍装饰器模式,会用以demo展示,同时会举出例子在IO系统中是如何呈现了这种模式,最后,我们探讨一下装饰器模式与代理模式之间的异同。笔者目前整理的一些blog针对面试都是超高频出现的。大家可以点击链接:http://blog.csdn.net/u012403290
装饰器模式
装饰器(Decorator )模式是指允许向一个现有的对象添加新的功能,同时又不改变其结构。一定意义上来说是继承的替代品。
比如说一幅画,我们给它上了一个边框。那么这个时候画还是那副画,但是它多了个边框,这个边框就是装饰器给它赋予的。但是如果你要通过继承来实现的话,就相当于是在画上,在画的周围给画上了边框,这个时候的改变就变成了静态的了,不够灵活的增加和撤销功能。
而且,还有一个极具重要的作用,比如上面说的这幅画,画和画框是相互耦合的,他们两者可以独立发展,画可以画的更精妙,边框也可以改的更好看,但是对他们组合的效果不会影响。
解决的问题
通过继承来扩展类的功能,逐渐冗余会产生很多的子类,或者保证原本的类的功能不变,需要额外的增加类的功能。
组织架构
下面这张图就是装饰器模式的设计方式:
我们解释一下每个类是干什么的:
1、component:是存在的一个抽象。可以是abstract类也可以是一个interface。也可以称为抽象被装饰类
2、concreteComponent:真实被装饰类,也是针对与component的具体实现。
3、Decorator:是装饰的一个抽象,可以称为抽象装饰类用abstract描述,因为它要包含一个真实对象的引用,无法用interface表示,而且必须要有和component一样的待实现方法。可以称为抽象装饰类
4、concreteDecorator:真实装饰类,具体的呈现对真实被装饰类的功能进行强化。
有的小伙伴就很疑惑了。
为什么真实被装饰类和抽象装饰类都要实现component接口呢?
因为装饰器只是进行装饰,东西还是原来那个东西。变形金刚可以变成人,但是改变不了他是汽车的根本,本质上来说它还是一辆车,只是多了类人的功能而已。
再者,我们需要对一个需要装饰的对象进行多次装饰。比如说一只猫是一半红色一半白色,那么我们就需要用白色装饰器装饰完接着用红色装饰器装饰。如果不实现这个接口,那么就无法嵌套装饰。
为什么装饰器还需要进行抽象呢?跳过Decorator类,直接由concreteDecorator来实现Component不就好了?
的确,如果是简单的装饰器模式来说,跳过Decorator也未尝不可,也不会影响既有功能。但是这样会违背面对对象编程的概念,如果新增的功能非常多,会产生很多相同的代码。
代码实现
存在对动物的描述,我们要给它赋予它的颜色。
animal类:描述一种动物
package com.brickworkers;
/**
*
* @author Brickworker
* Date:2017年5月12日下午4:10:54
* 关于类Animal.java的描述:抽象被装饰类
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public interface Animal {
//描述一种动物
public void name();
}
Cat类:描述具体的动物
package com.brickworkers;
/**
*
* @author Brickworker
* Date:2017年5月12日下午4:11:07
* 关于类Cat.java的描述:真实被装饰类
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public class Cat implements Animal{
@Override
public void name() {
System.out.println("猫");
}
}
Decorator类,抽象装饰器类:
package com.brickworkers;
/**
*
* @author Brickworker
* Date:2017年5月12日下午4:10:42
* 关于类Decorator.java的描述:抽象装饰类
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public abstract class Decorator implements Animal{
//一个真实对象的引用
private Animal animal;
//构造方法
public Decorator(Animal animal) {
this.animal = animal;
}
//把请求转发给被代理对象
public void name(){
animal.name();
}
}
RedDecorator类,红色动物装饰器:
package com.brickworkers;
/**
*
* @author Brickworker
* Date:2017年5月12日下午4:09:17
* 关于类RedDecorator.java的描述:具体装饰类
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public class RedDecorator extends Decorator{
//构造函数
public RedDecorator(Animal animal) {
super(animal);
}
//重写name方法,注入新的功能
@Override
public void name() {
super.name();
red();
}
//新增对动物颜色的描述
private void red(){
System.out.println("红色");
}
}
WhiteDecorator类,白色动物装饰器:
package com.brickworkers;
/**
*
* @author Brickworker
* Date:2017年5月12日下午4:10:24
* 关于类WhiteDecorator.java的描述:具体装饰类
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public class WhiteDecorator extends Decorator{
//构造函数
public WhiteDecorator(Animal animal) {
super(animal);
}
//重写name方法,注入新的功能
@Override
public void name() {
super.name();
white();
}
//新增对动物颜色的描述
private void white(){
System.out.println("白色");
}
}
以上就是一个完整的装饰器模式,接下来我们对它进行测试:
package com.brickworkers;
/**
*
* @author Brickworker
* Date:2017年5月12日下午4:11:50
* 关于类DecoratorTest.java的描述:装饰器类测试
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public class DecoratorTest {
public static void main(String[] args) {
//定义猫
Animal animal = new Cat();
animal.name();
System.out.println("==============================");
//红色的猫
RedDecorator redDecorator = new RedDecorator(animal);
redDecorator.name();
System.out.println("==============================");
//白色的猫
WhiteDecorator whiteDecorator = new WhiteDecorator(animal);
whiteDecorator.name();
System.out.println("==============================");
//一直白色的带红色的猫
RedDecorator redDecorator2 = new RedDecorator(new WhiteDecorator(animal));
redDecorator2.name();
System.out.println("==============================");
}
}
//运行结果:
//猫
//==============================
//猫
//红色
//==============================
//猫
//白色
//==============================
//猫
//白色
//红色
//==============================
//
//
如果要说装饰器模式复杂,那么复杂的一定是它一层层的嵌套。比如例子中的“一只白色带红色的猫”。但是你有没有想过我为什么描述成这样,而不描述成“一只红色带白的的猫”呢?在装饰器模式中,我们已最后一层来代表真实装饰的对象。比如说:
RedDecorator redDecorator = new RedDecorator(animal);
那么我们装饰的就是这只猫。但是如果这样:
RedDecorator redDecorator2 = new RedDecorator(new WhiteDecorator(animal));
那么我们装饰的其实是一只白色的猫。所以我上面的描述就是“一只白色带红色的猫”。
在IO系统中装饰器模式体现
我们以InputStream为例来说明装饰器模式,我在网上找了一些比较好的图:
InputStream
从图中,我们进行分析,或者自己去查看代码可以发现InputStream是祖宗,它就相当于我们前面说的component类,它是抽象的被装饰类。
在直接实现InputStream的子类中,我们发现FilterInputStream就是一个装饰器类,同时它有4个具体的装饰器子类。
那么按照我们前面的逻辑,我们要定义一个从File文件读入生产一个有行号标记的流要怎么写呢?从上面的图可以知道,我们其实需要对FileInputStream用LineNumberInputStream进行装饰,代码如下:
package com.brickworkers;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.LineNumberInputStream;
/**
*
* @author Brickworker
* Date:2017年5月12日下午4:11:50
* 关于类DecoratorTest.java的描述:装饰器类测试
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public class DecoratorTest {
public static void main(String[] args) throws FileNotFoundException {
//对FileInputStream用LineNumberInputStream进行装饰
LineNumberInputStream inputStream = new LineNumberInputStream(new FileInputStream(new File("C:/user/brickworker/test.txt")));
}
}
与代理模式的区别
装饰器模式与代理模式实现非常相似,都使用了组合的方式,而且装饰类与被装饰类都实现了同一个接口,代理类与被代理类也都实现了同一个接口。个人感觉而言总结了几点区别:
①功能不同,装饰器模式是为了更灵活的丰富对象功能;而代理模式是限制了对象访问。
②装饰器模式可以装饰多个对象,对象变动是灵活的。代理模式中代理类与被代理类是固定的,甚至是写死在代理类内部。