定义
装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
UML
角色
- Component: 抽象组件接口
- ConcreteComponent: 具体组件实现类
- Decorator: 由abstract申明的抽象装饰者类,实现了Component接口
- ConcreteDecorator: 抽象装饰者的具体实现,组合了Component对象,由构造器显式注入。
示例代码
/**
* desc : 组件接口
* Created by tiantian on 2018/10/5
*/
public interface Component {
void doSomething();
}
/**
* desc : 具体组件
* Created by tiantian on 2018/10/5
*/
public class ConcreteComponentA implements Component{
@Override
public void doSomething() {
System.out.println("hello");
}
}
/**
* desc : 具体组件
* Created by tiantian on 2018/10/5
*/
public class ConctreteComponentB implements Component{
@Override
public void doSomething() {
System.out.println("world");
}
}
/**
* desc : 抽象装饰者
* Created by tiantian on 2018/10/5
*/
public abstract class Decorator implements Component {
}
/**
* desc : 具体装饰者A
* Created by tiantian on 2018/10/5
*/
public class ConcteteDecoratorA extends Decorator {
private Component component;
public ConcteteDecoratorA(Component component) {
this.component = component;
}
@Override
public void doSomething() {
System.out.println("Decorating component object.");
component.doSomething();
System.out.println("Decorating component object again.");
}
}
/**
* desc : 具体装饰者
* Created by tiantian on 2018/10/5
*/
public class ConcteteDecoratorB extends Decorator {
private Component component;
public ConcteteDecoratorB(Component component) {
this.component = component;
}
@Override
public void doSomething() {
System.out.println("Decorating component object.");
component.doSomething();
System.out.println("Decorating component object again.");
}
}
/**
* desc : 测试
* Created by tiantian on 2018/10/5
*/
public class Test {
public static void main(String[] args) {
Component component = new ConcreteComponentA();
Component beDecoratedComp = new ConcteteDecoratorA(component);
beDecoratedComp.doSomething();
}
// Decorating component object.
// hello
// Decorating component object again.
}
与适配器区别
相同点:都是结构型设计模式。对象适配器has-a被适配者;装饰者同样has-a被装饰者。
一个对象可以由多个装饰者装饰,以增加不同的特性。
装饰者和被装饰对象拥有相同的接口,而对象适配器与适配目标接口相同,而非与被适配中接口相同。
适配器模式中,被适配对象同样也是与适配器组合起来使用,但目的是使接口适配,而非增加新的特性。
装饰者模式可以在运行时动态的为被装饰对象增加特性。
源码分析(装饰者在jdk IO中的应用)
// 示例代码
public class IOTest {
public static void main(String[] args) {
File file = new File("hello.txt");
InputStream is = new FileInputStream(file);
InputStreamReader reader = new InputStreamReader(is);
}
}
以下为jdk中InputStreamReader部分源码。
public class InputStreamReader extends Reader {
private final StreamDecoder sd;
/**
* Creates an InputStreamReader that uses the default charset.
*
* @param in An InputStream
*/
public InputStreamReader(InputStream in) {
super(in);
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
} catch (UnsupportedEncodingException e) {
// The default encoding should always be available
throw new Error(e);
}
}
// ... 省略jdk其他实现细节
}
通过源码可以了解到,InputStreamReader在构造自己时,通过装饰者StreamDecoder的静态方法forInputStreamReader把自己装饰为StreamDecoder对象。forInputStreamReader方法有个参数this用户传入自己。StreamDecoder和InputStreamReader都继承自Reader类,这正好也印证了装饰者和被装饰对象拥有相同接口的特点。