设计模式:装饰器模式应用
模式说明
-
装饰器模式
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
使用场景
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
多个装饰器可以组合使用,最后装饰出来的对象是应用了每个装饰器。
理解重点
- 有一个公共的接口,具体对象要实现这个接口,装饰器的(父)类也要实现它。 这样不论是具体对象还是装饰器,大家都是这个接口的实现。这也能让装饰器套装饰器,一层套一层修饰。
- 装饰器的父类要实现这个接口外,关键一点在于内部要持有这个具体对象(可以通过构造函数参数传递),持有后,它自己调用本身的方法,然后供装饰器装饰。
- 主要思路在于组合替代继承。把组件类 组合到装饰器内,在装饰器套用上相比继承更灵活机动。
类图一览
demo
计价算法,一个商品可能有多种优惠形式,比如红包、满减、优惠券等等。 当需要叠加使用时,最后的价格需要原始价格逐个使用优惠。
那么原始价就是一个被包装的对象,其他各种优惠就是装饰器。
这里订单对象包含了原始价格、优惠品类等信息。
订单类:Order
@Getter
@Setter
public class Order {
// 商品价格
BigDecimal price;
// 优惠品类
List<DiscountType> discountTypeList;
}
优惠品类枚举:DiscountType
public enum DiscountType {
RED_PACKAGE,
FREE,
COUPON;
}
通用计价接口:PriceCalc
public interface PriceCalc {
BigDecimal calcPrice(Order order);
}
原价计算类:OriginPrice
public class OriginPrice implements PriceCalc {
@Override
public BigDecimal calcPrice(Order order) {
return order.getPrice();
}
}
抽象装饰父类:ADiscount
public abstract class ADiscount implements PriceCalc {
// 持有被装饰对象
PriceCalc priceCalc;
public ADiscount(PriceCalc priceCalc) {
this.priceCalc = priceCalc;
}
@Override
public BigDecimal calcPrice(Order order) {
return priceCalc.calcPrice(order);
}
}
装饰类-优惠券类:Coupon
public class Coupon extends ADiscount {
public Coupon(PriceCalc priceCalc) {
super(priceCalc);
}
@Override
public BigDecimal calcPrice(Order order) {
// 计费规则
BigDecimal subtract = priceCalc.calcPrice(order).subtract(BigDecimal.valueOf(3));
order.setPrice(subtract);
return subtract;
}
}
装饰类-红包类:RedPackage
public class RedPackage extends ADiscount {
public RedPackage(PriceCalc priceCalc) {
super(priceCalc);
}
@Override
public BigDecimal calcPrice(Order order) {
// 计费规则
BigDecimal subtract = priceCalc.calcPrice(order).subtract(BigDecimal.valueOf(2));
order.setPrice(subtract);
return subtract;
}
}
装饰类-满减类:Free
public class Free extends ADiscount {
public Free(PriceCalc priceCalc) {
super(priceCalc);
}
@Override
public BigDecimal calcPrice(Order order) {
BigDecimal subtract = priceCalc.calcPrice(order).subtract(BigDecimal.valueOf(1));
order.setPrice(subtract);
return subtract;
}
}
测试类
public class Test {
public static void main(String[] args) {
Order order = new Order();
order.setPrice(BigDecimal.valueOf(10));
// 增加需要装饰的类
order.setDiscountTypeList(Arrays.asList(DiscountType.FREE, DiscountType.RED_PACKAGE, DiscountType.COUPON));
// 原始计价
PriceCalc priceCalc = new OriginPrice();
System.out.println("原价:" + priceCalc.calcPrice(order));
// 利用接口机制,层层装饰
for (DiscountType discountType : order.getDiscountTypeList()) {
if (discountType == DiscountType.COUPON) {
priceCalc = new Coupon(priceCalc);
} else if (discountType == DiscountType.RED_PACKAGE) {
priceCalc = new RedPackage(priceCalc);
} else if (discountType == DiscountType.FREE) {
priceCalc = new Free(priceCalc);
}
}
BigDecimal bigDecimal = priceCalc.calcPrice(order);
System.out.println("优惠后:" + bigDecimal);
}
}