装饰模式

106 阅读3分钟

一、需求(支付系统)

1、核心功能:“支付”
2、支持扩展一些附属功能,如:“支付” + “通知用户”,“支付” + “赠送购物券”,“支付” + “记录日志”
3、附属功能可以任意组合且可以任意调整执行顺序,如:“支付” + “通知用户” + “赠送购物券”,“支付” + “赠送购物券” + “通知用户”,“支付” + “通知用户” + “记录日志”,“支付” + “记录日志” + “通知用户”

二、实现

1、思路一:继承

1.1 核心功能 “支付” 代码

// 支付系统
public class PaymentSystem {
    // 支付功能
    public void pay(){
        System.out.println(this.toString() + " 支付");
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
       PaymentSystem paymentSystem = new PaymentSystem();
       paymentSystem.pay();
    }
}

控制台打印:PaymentSystem@610455d6:支付

1.2 新增“通知用户”功能

// 通知用户功能
public class NotifyUser extends PaymentSystem{
    public void notifyUser(){
        System.out.println(this.toString() + ":通知用户");
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
       NotifyUser notifyUser = new NotifyUser();
       notifyUser.pay();
       notifyUser.notifyUser();
    }
}

控制台打印:NotifyUser@511d50c0:支付
      NotifyUser@511d50c0:通知用户

1.3、新增“赠送购物券”或者“记录日志”功能或者任意组合
代码同上面差不多,都是新建子类

缺点: 每次新增附属功能或者更改组合顺序都要新建子类,导致类数目过于庞大难以维护

2、思路二:为实现对核心功能 “支付” 的复用,继承是通过改变“类”结构的方式实现的,但是继承的缺点前面已经提及了。可以尝试从“对象”的角度出发,复用已经存在的对象,也就是 “装饰模式”

2.1 代码实现

// 支付接口
public interface IPaymentSystem {
    void perform();
}
// 支付系统
public class PaymentSystem implements IPaymentSystem{

    // 支付功能
    public void pay(){
        System.out.println(this.toString() + ":支付");
    }

    @Override
    public void perform() {
        pay();
    }

}
// 通知用户功能
public class NotifyUser implements IPaymentSystem{

    private IPaymentSystem paymentSystem;

    public void wrap(IPaymentSystem paymentSystem){
        this.paymentSystem = paymentSystem;
    }

    public void notifyUser(){
        System.out.println(this.toString() + ":通知用户");
    }

    @Override
    public void perform() {
        if (paymentSystem != null){
            paymentSystem.perform();
        }
        notifyUser();
    }

}
// 赠送购物券
public class OfferCoupon implements IPaymentSystem{

    private IPaymentSystem paymentSystem;

    public void wrap(IPaymentSystem paymentSystem){
        this.paymentSystem = paymentSystem;
    }

    public void offer(){
        System.out.println(this.toString() + ":赠送购物券");
    }

    @Override
    public void perform() {
        if (paymentSystem != null){
            paymentSystem.perform();
        }
        offer();
    }

}
// 保存日志
public class SaveLog implements IPaymentSystem{

    private IPaymentSystem paymentSystem;

    public void wrap(IPaymentSystem paymentSystem){
        this.paymentSystem = paymentSystem;
    }

    public void save(){
        System.out.println(this.toString() + ":保存日志");
    }

    @Override
    public void perform() {
        if (paymentSystem != null){
            paymentSystem.perform();
        }
        save();
    }

}
// 客户端
public class Client {
    public static void main(String[] args) {
        PaymentSystem paymentSystem = new PaymentSystem();

        NotifyUser notifyUser = new NotifyUser();
        notifyUser.wrap(paymentSystem);

        OfferCoupon offerCoupon = new OfferCoupon();
        offerCoupon.wrap(notifyUser);

        SaveLog saveLog = new SaveLog();
        saveLog.wrap(offerCoupon);

        saveLog.perform();
    }
}

控制台打印:PaymentSystem@610455d6:支付
      NotifyUser@511d50c0:通知用户
      OfferCoupon@60e53b93:赠送购物券
      SaveLog@5e2de80c:保存日志

缺点: 在NotifyUser,OfferCoupon,SaveLog三个类中存在诸多重复代码,具体如下

F127C361-08D6-4232-BFDA-53DA88E26917.png

改进: 把重复代码抽取到一个抽象父类当中,三个子类只需实现新增功能的抽象方法

public abstract class AbstractPayment implements IPaymentSystem{
    
    private IPaymentSystem paymentSystem;

    public void wrap(IPaymentSystem paymentSystem){
        this.paymentSystem = paymentSystem;
    }

    // 添加新功能
    public abstract void addNewFeature();
    
    @Override
    public void perform() {
        if (paymentSystem != null){
            paymentSystem.perform();
        }
        addNewFeature();
    }
}

// 通知用户功能
public class NotifyUser extends AbstractPayment{
    
    @Override
    public void addNewFeature() {
        notifyUser();
    }

    public void notifyUser(){
        System.out.println(this.toString() + ":通知用户");
    }

}
// 赠送购物券
public class OfferCoupon extends AbstractPayment{
    
    @Override
    public void addNewFeature() {
        offer();
    }

    public void offer(){
        System.out.println(this.toString() + ":赠送购物券");
    }

}
// 保存日志
public class SaveLog extends AbstractPayment{
    
    @Override
    public void addNewFeature() {
        save();
    }

    public void save(){
        System.out.println(this.toString() + ":保存日志");
    }

}

三、总结

“装饰模式”本质上实现了一条“对象链”。每一个对象节点都持有一个后继对象节点,且被一个前驱对象节点持有(除首尾对象节点外)。该链条可以任意数量、任意顺序组合。该模式适用于在核心功能基础上新增其他附属功能且附属功能数量与调用顺序变化较多的场景中。