一、装饰者模式
一句话总结:增强原有对象,而不改变原有对象
- 定义:在
不改变原有对象的基础上,将功能附加到对象上
,提供了比继承更有弹性的替代方案(扩展原有对象功能) - 类型:结构型
- 适用场景:
- 扩展一个类的功能或给一个类添加附加职责
- 动态的给一个对象添加功能,这些功能可以再动态的撤销
- 优点:
- 继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能
- 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同的效果
- 符合
开闭原则
- 缺点:
- 会出现更多的代码,更多的类,增加程序的复杂性
- 动态装饰时和多层装饰时会更复杂
- 相关设计模式:
- 装饰者模式和
代理模式
:装饰者模式主要关注在一个对象上动态的添加方法
,而代理模式关注于控制对对象的访问
,代理模式的代理类可以对其客户隐藏一个对象的具体信息,通常使用代理模式时,会在代理类中创建一个对象的实例,而装饰者模式的时候,通常会把原始对象作为一个参数传给装饰者的构造器 - 装饰者模式和
适配器模式
:都可以称为包装模式
,装饰者和被装饰者可以实现相同的接口或者装饰者是被装饰者的子类,在适配器模式中,适配器和被适配的类具有不同的接口
二、Coding
反例
public class Battercake {
protected String getDesc(){
return "煎饼";
}
protected int cost(){
return 8;
}
}
public class BattercakeWithEgg extends Battercake {
@Override
public String getDesc() {
return super.getDesc()+" 加一个鸡蛋";
}
@Override
public int cost() {
return super.cost() + 1;
}
}
public class BattercakeWithEggSausage extends BattercakeWithEgg {
@Override
public String getDesc() {
return super.getDesc()+ " 加一根香肠";
}
@Override
public int cost() {
return super.cost() + 2;
}
}
public class Test {
public static void main(String[] args) {
Battercake battercake = new Battercake();
System.out.println(battercake.getDesc()+" 销售价格:"+battercake.cost());
Battercake battercakeWithEgg = new BattercakeWithEgg();
System.out.println(battercakeWithEgg.getDesc()+" 销售价格:"+battercakeWithEgg.cost());
Battercake battercakeWithEggSausage = new BattercakeWithEggSausage();
System.out.println(battercakeWithEggSausage.getDesc()+" 销售价格:"+battercakeWithEggSausage.cost());
}
}
煎饼 销售价格:8
煎饼 加一个鸡蛋 销售价格:9
煎饼 加一个鸡蛋 加一根香肠 销售价格:11
- 说明:这个时候又有个顾客需要加两个鸡蛋和加两根香肠的煎饼,发现无法得到满足,导致这场买卖做不下去,因为系统不支持。看这段代码其实很简单,简单的继承关系,但是对于煎蛋的组合非常多,如果单单使用继承是无法做到灵活处理的,如何做到更灵活优雅,这个时候就需要我们的装饰者模式上场了。
正例
public abstract class ABattercake {
protected abstract String getDesc();
protected abstract int cost();
}
public class Battercake extends ABattercake {
@Override
protected String getDesc() {
return "煎饼";
}
@Override
protected int cost() {
return 8;
}
}
public abstract class AbstractDecorator extends ABattercake {
private ABattercake aBattercake;
public AbstractDecorator(ABattercake aBattercake) {
this.aBattercake = aBattercake;
}
protected abstract void doSomething();
@Override
protected String getDesc() {
return this.aBattercake.getDesc();
}
@Override
protected int cost() {
return this.aBattercake.cost();
}
}
public class EggDecorator extends AbstractDecorator {
public EggDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
protected void doSomething() {
}
@Override
protected String getDesc() {
return super.getDesc() + " 加一个鸡蛋";
}
@Override
protected int cost() {
return super.cost() + 1;
}
}
public class SausageDecorator extends AbstractDecorator{
public SausageDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
protected void doSomething() {
}
@Override
protected String getDesc() {
return super.getDesc() + " 加一根香肠";
}
@Override
protected int cost() {
return super.cost() + 2;
}
}
public class Test {
public static void main(String[] args) {
ABattercake aBattercake = new Battercake();
aBattercake = new EggDecorator(aBattercake);
aBattercake = new EggDecorator(aBattercake);
aBattercake = new SausageDecorator(aBattercake);
System.out.println(aBattercake.getDesc() + " 销售价格:" + aBattercake.cost());
}
}
煎饼 加一个鸡蛋 加一个鸡蛋 加一根香肠 销售价格:12
- UML类图
- 说明:首先有个抽象煎饼,实体煎饼继承抽象煎饼,然后有个抽象的装饰角色,而鸡蛋和肠就是实体的装饰角色,我们可以先摊一个煎饼(new Battercake()),要加鸡蛋几个鸡蛋几根肠,可以将煎饼传给装饰者,让装饰者给其加料,无限次的扩展功能。
- 注意:其实
抽象煎饼和抽象装饰者也非必须
,要看实际场景是否需要抽象类扩展功能。
三、源码中的应用
- java.io.BufferedReader:如以下代码所示,Reader是一个抽象类,BufferedReader把Reader组合到自己的类中,它的构造器将Reader传入构造BufferedReader,BufferedReader就可以对Reader进行装饰
public class BufferedReader extends Reader {
private Reader in;
...
public BufferedReader(Reader in, int sz) {
super(in);
if (sz <= 0)
throw new IllegalArgumentException("Buffer size <= 0");
this.in = in;
cb = new char[sz];
nextChar = nChars = 0;
}
...
- org.apache.ibatis.cache.Cache:可以看到这个文件夹下的类都是Cache的装饰者