设计模式之装饰者模式

举报
炒香菇的书呆子 发表于 2022/05/20 23:54:15 2022/05/20
【摘要】 概念优点:装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果装饰器模式完全遵守开闭原则缺点:装饰器模式会增加许多子类,过度使用会增加程序得复杂性。为什么不使用继承来搞?通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。装...

概念

优点

  • 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
  • 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
  • 装饰器模式完全遵守开闭原则

缺点

  • 装饰器模式会增加许多子类,过度使用会增加程序得复杂性。

为什么不使用继承来搞?

通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。

装饰者模式的目标

使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能。

装饰者模式使用场景

  • 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
  • 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现。
  • 当对象的功能要求可以动态地添加,也可以再动态地撤销时。

定义角色
目标对象:被装饰的原始对象;
装饰者抽象:抽象的装饰者;
具体装饰者:装饰者抽象的具体实现。

代码示例

订单类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    private Double price;
    private Integer orderId;
    private String orderNo;
    private Double pay;
    private List<Integer> promotionTypeList;
}

活动的枚举类,用于记录活动类型

/**
 * 活动枚举
 */
public enum PromotionType {
    COUPON(1),
    VIP(2);
    private int type;

    PromotionType(final int type){
        this.type = type;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }
}

装饰订单的装饰者接口

/**
 * 订单装饰者接口
 */
public interface OrderDecorator {
    //抽象装饰方法
    Order decorator(Order order);
}

订单装饰者实现类–优惠券订单装饰者

/**
 * 优惠券装饰者
 */
public class CouponOrderDecorator implements OrderDecorator {
    @Override
    public Order decorator(Order order) {
        System.out.println("优惠券减去3元");
        order.setPay(order.getPrice() - 3);
        order.setPrice(order.getPrice() - 3);
        System.out.println("实付:" + order.getPay());
        return order;
    }
}

订单装饰者实现类–VIP订单装饰者

/**
 * vip装饰者
 */
public class VipOrderDecorator implements OrderDecorator {
    @Override
    public Order decorator(Order order) {
        System.out.println("vip减去1元");
        order.setPay(order.getPrice() - 1);
        order.setPrice(order.getPrice() - 1);
        System.out.println("实付:" + order.getPay());
        return order;
    }
}

下面就看看具体怎么使用吧

public class DecoratorClient {
    public static void main(String[] args) {
        //创建一个原始订单
        Order order = new Order(10d, 1, "0001", 10d, null);
        //创建装饰者子类对象
        OrderDecorator vipOrderDecorator = new VipOrderDecorator();
        OrderDecorator couponOrderDecorator = new CouponOrderDecorator();
        //使用vip和优惠券包装原订单对象,使其拥有vip和优惠券的优惠。
        //在某些情况下(根据具体需求来实现)还可以根据包装的顺序不同而获得不同的结果。
        Order vipCouponOrder = couponOrderDecorator.decorator(vipOrderDecorator.decorator(order));
    }
}

装饰者模式在开源代码中的使用

java.io中的应用

装饰者模式在Java语言中的最著名的应用莫过于Java I/O标准库的设计了。例如,InputStream 的子类FilterInputStreamOutputStream的子类 FilterOutputStreamReader的子类 BufferedReader以及FilterReader,还有Writer 的子类 BufferedWriterFilterWriter以及 PrintWriter等,它们都是抽象装饰类。

下面看一个例子,BufferedReader实现装饰者的方式是继承Reader类,然后将Reader类定义为自己的成员变量。这里的Reader即是装饰者抽象,又是目标对象的抽象,BufferedReader是具体装饰者,又是目标对象。

FilterReader的方式和BufferedReader的实现方式一样

为什么BufferedReader没有使用上面示例代码的实现方式呢?

这其实就是老生常谈的–设计模式是一种思想,实现方式有多种多样。BufferedReader类内部没有类似示例代码中的decorator()方法,所以它通过将目标对象定义为成员变量的方式来引入目标对象(而不是将目标对象定义形参的方式),从而达到将其他Reader实现类的功能装饰到自身的目的。

这里只是简单的说明了这种实现装饰者的用法,具体的内部源码大家可以自己去看一下,方法的内部最终还是调用了in.xxx方法。

public static void main(String[] args) throws IOException {
        Reader file = new FileReader("xxx路径");
        //FilterReader中使用FileReader进行装饰
        FilterReader filterReader = new PushbackReader(file);
        //BufferedReader中使用FilterReader进行装饰
        Reader bufferedReader = new BufferedReader(filterReader);
        //最终调用BufferedReader的read,close方法
        //就获得了FilterReader以及FileReader的功能
        bufferedReader.read();
        bufferedReader.close();
    }

shenyu网关中的应用

shenyu上下文装饰者接口,主要用于根据不同的接入类型来组装不同的上下文信息。

shenyugrpc上下文装饰者实现类,这种实现方式就和实例代码一样,有一个decorator()方法将目标对象ShenyuContext当做形参传入,然后在该方法内对ShenyuContext进行grpc类型的装饰。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200