设计模式--装饰者模式
今天偶然的机会接触到了装饰者模式,当我们需要很多的子类组合去实现一个功能时,可以考虑下使用装饰者模式。例如衣服有尺寸、规格、颜色,咖啡有种类、糖度、温度,这样的动态组合会衍生出指数增长的子类,装饰者模式就适用于这样的情景。
1、适用场景
- 使用子类拓展一个类的功能呈现爆炸性增长,请改为装饰者模式
- 动态撤销、增加一个类的功能,各个功能类相互独立。
2、角色
- 抽象构件角色(Component):定义一个接口或抽象类,装饰者角色通过它实现给具体的构件角色赋能,例如衣服。
- 具体构件角色(ConcreteComponent):实现或继承抽象构件角色,被装饰者角色赋能的对象,例如裤子、外套。
- 抽象装饰者角色(Decorator): 同样实现或继承抽象构件角色,一般情况下是一个抽象类,用来拓展抽象构件角色的功能,例如拓展衣服的尺寸。
- 具体装饰者角色(ConcretDecorator) :继承于抽象装饰者角色,实现具体的赋能动作,例如尺寸为 XL 。
组件构成参考博客:装饰者角色组成
看了上面的描述你可能还会有点懵,请看下面的构图:
看完之后是否有一定的印象了呢? ConcreteComponent 及 ConcreteDecorator 都可以拓展,且相互独立,这便是装饰者模式。
3、例子
以衣服的材质、价格为例子演示下装饰者模式。
衣服--抽象构件角色(Component)
package com.lin.decorator;
import java.math.BigDecimal;
/**
* 抽象构件角色(Component),衣服总称
*/
public abstract class Clothes {
String description = "clothes";
/**
* 衣服的描述
*
* @return 描述,具体构件角色可自定义描述
*/
public String getDescription() {
return description;
}
/**
* 衣服的价格,由具体构件角色实现
*
* @return 价格
*/
public abstract BigDecimal getCost();
}
裤子--具体构件角色
package com.lin.decorator;
import java.math.BigDecimal;
/**
* 具体构件角色(ConcreteComponent),裤子
*/
public class Pants extends Clothes{
/**
* 裤子的描述
*/
public Pants() {
description = "Pants";
}
/**
* 裤子的价格,此时是装饰前的。
*
* @return
*/
@Override
public BigDecimal getCost() {
return new BigDecimal(40.0);
}
}
背心--具体构件角色
package com.lin.decorator;
import java.math.BigDecimal;
/**
* 具体构件角色(ConcreteComponent),背心
*/
public class Vest extends Clothes{
/**
* 背心的描述
*/
public Vest() {
description = "Vest";
}
/**
* 背心的价格,此时是装饰前的。
*
* @return 价格
*/
@Override
public BigDecimal getCost() {
return new BigDecimal(40.0);
}
}
衣服材质--抽象装饰者角色
package com.lin.decorator;
import java.math.BigDecimal;
/**
* 抽象装饰者角色(Decorator), 衣服的材质
*/
public abstract class MaterialDecorator extends Clothes {
/**
* 由具体装饰者角色(ConcreteDecorator)实现每一次的描述
*
* @return
*/
public abstract String getDescription();
/**
* 由具体装饰者角色(ConcreteDecorator)实现每一次的价格计算
*
* @return
*/
public abstract BigDecimal getCost();
}
雪纺--具体装饰者角色
package com.lin.decorator;
import java.math.BigDecimal;
/**
* 具体装饰者角色(ConcreteDecorator),衣服的材质为雪纺
*/
public class Chiffon extends MaterialDecorator {
private Clothes clothes;
/**
* 保存每一次雪纺装饰的构件角色
*
* @param clothes
*/
public Chiffon(Clothes clothes) {
this.clothes = clothes;
}
/**
* 保留每一次雪纺装饰的描述
*
* @return 描述
*/
@Override
public String getDescription() {
return clothes.getDescription() + " 雪纺";
}
/**
* 保留每一次雪纺装饰的价格
*
* @return 价格
*/
@Override
public BigDecimal getCost() {
return new BigDecimal(30.0).add(clothes.getCost());
}
}
纤维--具体装饰者角色
package com.lin.decorator;
import java.math.BigDecimal;
/**
* 具体装饰者角色(ConcreteDecorator),衣服的材质为纤维
*/
public class Fibre extends MaterialDecorator {
private Clothes clothes;
/**
* 保存每一次纤维装饰的构件角色
*
* @param clothes
*/
public Fibre(Clothes clothes) {
this.clothes = clothes;
}
/**
* 保留每一次纤维装饰的描述
*
* @return 描述
*/
@Override
public String getDescription() {
return clothes.getDescription() + " 纤维";
}
/**
* 保留每一次纤维装饰的价格
*
* @return 价格
*/
@Override
public BigDecimal getCost() {
return new BigDecimal(50.0).add(clothes.getCost());
}
}
测试类
package com.lin.controller;
import com.lin.decorator.*;
public class test {
public static void main(String[] args) {
Clothes clothes1 = new Pants();
System.out.println(clothes1.getDescription() + " 价格为:" + clothes1.getCost());
Clothes clothes2 = new Pants();
clothes2 = new Fibre(clothes2);
System.out.println(clothes2.getDescription() + " 价格为:" + clothes2.getCost());
Clothes clothes3 = new Vest();
clothes3 = new Chiffon(clothes3);
System.out.println(clothes3.getDescription() + " 价格为:" + clothes3.getCost());
}
}
运行结果如下:
Pants 价格为:40
Pants 纤维 价格为:90
Vest 雪纺 价格为:70
装饰者模式的写法多种多样,寻找自己易于理解的点实现类似的功能即可。
4、缺点
1、增加了抽象装饰者类和具体装饰者类,一定程度增加了系统的复杂度,加大了系统的学习和理解成本。
2、灵活性也意味着更容易出错,对于多次被多次修饰的对象,调试时寻找错误可能需要找到多个地方。