一、概念
1、定义:在不改变原有对象的基础上,动态地将“责任”附加到对象上。装饰者提供了比继承更有弹性的替代方案(扩展原有对象功能)
2、类型:结构型
3、适用场景
- 扩展一个类的功能,或给一个类添加职责
- 动态的给一个对象添加功能。并且这些功能可以动态的撤销
4、优缺点
优点
- 比继承更加灵活,在不改变原有对象的基础之上给一个对象扩展功能(原来是用继承来扩展功能,如果功能繁多,必然要用到很多子类,增加系统复杂性,并且使用继承实现功能扩展是静态的,我们必须可预见这些扩展功能,装饰者模式是动态的添加职责!)
- 通过使用不同的装饰类,以及这些装饰类的组合,可以达到不同的效果。
- 符合OCP,可以扩展行为,装饰者和被装饰者都能独立变化,原有代码不用改变。
缺点
- 增加更多的类,增加程序复杂性
- 动态装饰和多层装饰会更加复杂
5、相关设计模式
-
装饰者模式和代理模式
职责不同,装饰者模式是在一个对象上动态的添加方法,代理模式是控制对对象的访问,代理也能做功能的增强,装饰者更多考虑的是功能扩展。代理模式可以为它的客户隐藏对象的具体信息,此外,使用装饰者模式是把原始对象传进构造器,而代理模式是创建一个对象的实例。
-
装饰者模式和适配器模式
装饰者和适配器模式都是包装模式(Wrapper Pattern),装饰者也是一种特殊的代理模式。
装饰者模式 | 适配器模式 | |
---|---|---|
形式 | 是一种非常特别的适配器模式 | 没有层级关系,装饰器模式有层级关系 |
定义 | 装饰者和被装饰者都实现同一个接口,主要目的是为了扩展之后依旧保留 OOP 关系 | 适配器和被适配者没有必然的联系,通常是采用继承或代理的形式进行包装 |
关系 | 满足 is-a 的关系 | 满足 has-a 的关系 |
功能 | 注重覆盖、扩展 | 注重兼容、转换 |
设计 | 前置考虑 | 后置考虑 |
5、UML
装饰者和被装饰者必须是一样的类型,它们继承了共同的超类,这是关键,利用继承获得了类型匹配,而不是继承它的行为,行为是来自装饰者自身以及其他的基础组件,或者是装饰者-装饰者之间的组合关系。
PS:图中最顶部component类型是抽象类,当然java中也可以使用接口
二、Coding
背景,买鸡蛋饼,加鸡蛋,加香肠。。。
我们要有抽象的实体类、确定的实体类,以及抽象的装饰者,确定的装饰者
被装饰得实体是煎饼,装饰者是鸡蛋和香肠
// 抽象实体类
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();
}
}
// 具体装饰者1
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;
}
}
// 具体装饰者2
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;
aBattercake = new Battercake();
aBattercake = new EggDecorator(aBattercake);
aBattercake = new EggDecorator(aBattercake);
aBattercake = new SausageDecorator(aBattercake);
System.out.println(aBattercake.getDesc() + " " + aBattercake.cost());
}
}
//煎饼 加一个鸡蛋 加一个鸡蛋 加一根香肠:12
抽象的装饰者有没有必要,要看具体的业务场景,不是抽象的方法也没有关系,如果具体的装饰者必须要实现某个职责,可以用抽象的。