技术成神之路:设计模式(二十)装饰模式

介绍

装饰模式(Decorator Pattern)是一种结构型设计模式,它允许在不改变对象自身的情况下,动态地为对象添加额外的职责。这个模式通常用于增强或改变对象的功能。

1.定义


装饰模式通过创建一个装饰类,将功能动态地添加到被装饰类的实例中。装饰类与被装饰类实现相同的接口或继承相同的父类,这样装饰对象就可以取代被装饰对象。

2. 主要作用


  • 动态地给一个对象添加额外的职责,而不改变其原本的结构。
  • 提供了比继承更灵活的替代方案,通过组合而不是继承来扩展对象的功能。

3. 解决的问题


  • 需要在运行时为对象动态添加功能,而不修改对象本身。
  • 需要避免因类数量过多而导致的复杂性。
  • 需要在不影响其他对象的情况下扩展功能。

4. 模式原理


包含角色:

  1. 组件接口(Component): 定义了一个接口,用于所有具体组件和装饰器。
  2. 具体组件(ConcreteComponent): 实现了组件接口的具体类。
  3. 装饰器(Decorator): 实现了组件接口并持有一个组件的引用,用于在其方法中调用被装饰对象的方法。
  4. 具体装饰器(ConcreteDecorator): 继承自装饰器类,添加额外的功能。

UML类图:

代码示例:

java 复制代码
// Component接口
public interface Coffee {
    double cost();
    String description();
}

// ConcreteComponent具体组件
public class SimpleCoffee implements Coffee {
    @Override
    public double cost() {
        return 5;
    }

    @Override
    public String description() {
        return "Simple Coffee";
    }
}

// Decorator装饰器
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }

    @Override
    public double cost() {
        return decoratedCoffee.cost();
    }

    @Override
    public String description() {
        return decoratedCoffee.description();
    }
}

// ConcreteDecorator具体装饰器
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double cost() {
        return super.cost() + 1.5;
    }

    @Override
    public String description() {
        return super.description() + ", Milk";
    }
}

public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double cost() {
        return super.cost() + 0.5;
    }

    @Override
    public String description() {
        return super.description() + ", Sugar";
    }
}

调用

java 复制代码
public class DecoratorPatternExample {
    public static void main(String[] args) {
        Coffee simpleCoffee = new SimpleCoffee();
        System.out.println(simpleCoffee.description() + " $" + simpleCoffee.cost());

        Coffee milkCoffee = new MilkDecorator(simpleCoffee);
        System.out.println(milkCoffee.description() + " $" + milkCoffee.cost());

        Coffee milkSugarCoffee = new SugarDecorator(milkCoffee);
        System.out.println(milkSugarCoffee.description() + " $" + milkSugarCoffee.cost());
    }
}

打印输出

c 复制代码
Simple Coffee \$5.0
Simple Coffee, Milk \$6.5
Simple Coffee, Milk, Sugar \$7.0

上面示例,通过使用装饰者模式,程序能够灵活地添加新功能而不需要修改原有的 Coffee 实现。

装饰模式看似很简单,其实一点也不复杂😁,我相信看完一遍示例,再回头看下定义就会全懂了。

那么看似一个简单的设计模式,在我们开发中有哪些应用场景呢?下面举几个经典示例来加深下印象:

1.Java的输入/输出(I/O)库中使用了装饰者模式来增强流的功能。

例如,BufferedInputStreamDataInputStream 是装饰者,它们分别装饰 InputStream 类。通过这些装饰者,可以为基础流添加缓冲、数据格式等额外的功能。

emm... 上一篇文章,桥接模式也是举的这个例子,看来Java SDK使用的设计模式不少啊。

java 复制代码
InputStream fileStream = new FileInputStream("file.txt");
BufferedInputStream bufferedStream = new BufferedInputStream(fileStream);
DataInputStream dataStream = new DataInputStream(bufferedStream);

2.Android View 组件

安卓开发的同学肯定都自定义过View吧,例如,创建一个自定义的TextView,通过扩展原始的TextView并添加一些额外的样式或功能。

c 复制代码
public class CustomTextView extends TextView {
    public CustomTextView(Context context) {
        super(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 添加自定义绘制逻辑
    }
}

还有在 使用 Retrofit 网络请求时,可以自定义的拦截器,Spring 框架的 AOP通过代理(proxy)对象,可以在不改变原始对象的情况下,动态地添加横切关注点(如日志记录、事务管理等)等等... 这些都是装饰模式的经典案例。可能我们平时没注意,不知道它们用的是装饰模式,只要记住只要是 在运行时动态地添加行为和功能就可以认为是装饰者模式即可。

5. 优缺点


优点:

  • 灵活性:可以动态地添加或修改对象的功能,而无需修改原始类。
  • 扩展性:可以通过添加新的装饰器类来扩展功能,不需要改变现有代码。
  • 符合开闭原则:原有类不需要改变,新增功能通过装饰器实现。

缺点:

  • 复杂性:使用装饰模式会增加系统的复杂性,特别是在多个装饰器层叠使用时。
  • 调试困难:由于装饰者的层级结构,调试时可能不容易追踪到具体的功能实现。

6. 应用场景


  • 在图形界面中,可以为组件如按钮、文本框等添加滚动条、边框等装饰。
  • 在Java的IO系统中,装饰模式被广泛应用于各种输入流和输出流的实现中,如BufferedInputStream装饰FileInputStream。
  • 需要在不改变原有类的情况下,动态地为对象添加功能。

7. 总结


装饰模式是一种强大的设计模式,通过组合对象和装饰器的方式实现功能的动态扩展。它在实际开发中非常有用,能够有效地提高代码的灵活性和可维护性。虽然引入了额外的复杂性,但在许多情况下,这种复杂性是值得的,因为它使得系统可以更容易地适应变化和需求。

相关推荐
SoraLuna26 分钟前
「Mac畅玩鸿蒙与硬件47」UI互动应用篇24 - 虚拟音乐控制台
开发语言·macos·ui·华为·harmonyos
xlsw_31 分钟前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹1 小时前
基于java的改良版超级玛丽小游戏
java
Dream_Snowar2 小时前
速通Python 第三节
开发语言·python
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭2 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫2 小时前
泛型(2)
java
超爱吃士力架2 小时前
邀请逻辑
java·linux·后端
南宫生2 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石2 小时前
12/21java基础
java
高山我梦口香糖2 小时前
[react]searchParams转普通对象
开发语言·前端·javascript