Java设计模式:深入装饰器模式的三种写法(六)

简介: Java设计模式:深入装饰器模式的三种写法(六)

前言

Java中的装饰器模式是一种结构型设计模式,它允许你在不修改现有类的情况下,动态地将新功能添加到对象上。装饰器模式通过创建一个包装了原始对象的装饰器类来实现这一点。装饰器类与原始类具有相同的接口,因此它们可以互换使用。

一、装饰器模式的主要组成

  1. 抽象组件(Component):定义了一个接口,用于规定具体组件和装饰器类的共同行为。
  2. 具体组件(ConcreteComponent):实现了抽象组件接口,它是被装饰的原始对象,通常可以独立使用。
  3. 抽象装饰器(Decorator):这是一个抽象类,它实现了抽象组件接口,并包含一个对抽象组件的引用。抽象装饰器通常提供对所有方法的默认实现,这些方法通常只是简单地调用被装饰对象(即抽象组件引用)上的相应方法。
  4. 具体装饰器(ConcreteDecorator):这是抽象装饰器的子类,它负责添加新的功能。具体装饰器可以重写父类(抽象装饰器)的方法,以在被装饰对象的方法调用前后增加额外的行为。

二、装饰器模式的优点

  • 灵活性:装饰器模式提供了比继承更灵活的扩展方式。你可以通过组合多个装饰器来创建具有各种功能的对象,而无需修改原始类或使用大量的子类。
  • 避免类爆炸:当使用继承来添加功能时,每个新功能都可能需要一个新的子类。这可能导致类数量的快速增长,使系统变得复杂且难以维护。装饰器模式通过动态地添加功能来避免这个问题。
  • 开闭原则:装饰器模式符合开闭原则,即对扩展开放,对修改关闭。你可以添加新的装饰器来扩展功能,而无需修改现有的代码。

三、装饰器模式的局限

  • 额外的复杂性:使用装饰器模式可能会增加系统的复杂性,因为你需要管理额外的装饰器类和对象。此外,理解装饰器之间的交互和它们如何影响被装饰对象的行为可能需要一些努力。
  • 类型匹配问题:在某些情况下,装饰器可能会破坏类型匹配。例如,如果你有一个需要特定类型参数的方法,并且你传递了一个被装饰的对象(其类型是装饰器类型),那么可能会出现类型不匹配的问题。这可以通过使用接口和泛型来缓解。
  • 额外的性能开销:由于装饰器可能会创建多层对象包装,因此在某些情况下可能会引入额外的性能开销。

四、装饰器模式的使用场景

  • 需要动态地添加或撤销功能:当你需要在运行时根据需要动态地添加或撤销功能时,装饰器模式是一个很好的选择。例如,你可以创建一个装饰器来记录方法调用的日志,然后在需要时将其应用到对象上。
  • 避免使用大量的子类:当你想要扩展一个类的功能,但又不希望创建大量的子类时,可以使用装饰器模式。通过创建装饰器类来添加新功能,你可以避免类数量的快速增长。
  • 需要透明的扩展功能:装饰器模式允许你在不修改原始类的情况下透明地扩展功能。这意味着你可以在不改变客户端代码的情况下添加新功能。客户端代码可以继续使用原始类的接口,而无需了解装饰器的存在。

五、装饰器模式的三种写法

方式1️⃣:使用接口和抽象类【标准实现】

下面是一个简单的Java装饰器模式的实现:

步骤 1:定义抽象组件
public interface Coffee {
    double getCost();
    String getIngredients();
}
步骤 2:创建具体组件
public class SimpleCoffee implements Coffee {
    @Override
    public double getCost() {
        return 1;
    }

    @Override
    public String getIngredients() {
        return "Coffee";
    }
}
步骤 3:定义抽象装饰器
public abstract class CoffeeDecorator implements Coffee {
    protected final Coffee decoratedCoffee;

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

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

    @Override
    public String getIngredients() {
        return decoratedCoffee.getIngredients();
    }
}
步骤 4:创建具体装饰器
public class MilkCoffee extends CoffeeDecorator {
    public MilkCoffee(Coffee coffeeToDecorate) {
        super(coffeeToDecorate);
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 0.5;
    }

    @Override
    public String getIngredients() {
        return decoratedCoffee.getIngredients() + ", Milk";
    }
}

public class WhipCoffee extends CoffeeDecorator {
    public WhipCoffee(Coffee coffeeToDecorate) {
        super(coffeeToDecorate);
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 0.7;
    }

    @Override
    public String getIngredients() {
        return decoratedCoffee.getIngredients() + ", Whip";
    }
}
步骤 5:客户端代码
public class CoffeeShop {
    public static void main(String[] args) {
        Coffee simpleCoffee = new SimpleCoffee();
        System.out.println("Cost: " + simpleCoffee.getCost() + "; Ingredients: " + simpleCoffee.getIngredients());

        Coffee milkCoffee = new MilkCoffee(simpleCoffee);
        System.out.println("Cost: " + milkCoffee.getCost() + "; Ingredients: " + milkCoffee.getIngredients());

        Coffee milkWhipCoffee = new WhipCoffee(milkCoffee);
        System.out.println("Cost: " + milkWhipCoffee.getCost() + "; Ingredients: " + milkWhipCoffee.getIngredients());
    }
}


运行上面的客户端代码,你会看到不同的咖啡组合和它们的价格以及配料。

方式2️⃣:使用内部类

有时候,装饰器可能只针对某个特定的组件类有用。在这种情况下,可以将装饰器类作为组件类的内部类来实现。

public class Component {
    public void operation() {
        // ...
    }

    public class Decorator extends Component {
        @Override
        public void operation() {
            super.operation();
            // Additional functionality
        }
    }
}

然后客户端可以这样使用:

Component component = new Component();
Component decoratedComponent = component.new Decorator();
decoratedComponent.operation();

这种方式不太常见,因为它将装饰器和被装饰的组件紧密耦合在一起。然而,在某些情况下,如果装饰器的逻辑与被装饰的组件紧密相关,并且不打算与其他组件共享,这种方式可能是合适的。

方式3️⃣:使用Java 8的函数式接口和Lambda表达式

在Java 8及更高版本中,可以利用函数式接口和Lambda表达式来更简洁地实现装饰器模式。例如,可以定义一个函数式接口来表示组件的操作:

@FunctionalInterface
public interface ComponentOperation {
    void execute();
}

然后创建一个具体的组件实现:

public class ConcreteComponent implements ComponentOperation {
    @Override
    public void execute() {
        System.out.println("Executing operation in ConcreteComponent");
    }
}

现在,可以创建一个装饰器类,它接受一个ComponentOperation并添加额外的行为:

public class Decorator implements ComponentOperation {
    private final ComponentOperation componentOperation;
    private final Runnable additionalBehavior;

    public Decorator(ComponentOperation componentOperation, Runnable additionalBehavior) {
        this.componentOperation = componentOperation;
        this.additionalBehavior = additionalBehavior;
    }

    @Override
    public void execute() {
        additionalBehavior.run(); // This could be before or after the component operation
        componentOperation.execute();
        // additionalBehavior.run(); // Optionally call additional behavior after the operation
    }
}

客户端代码可以像这样使用装饰器:

public class Client {
    public static void main(String[] args) {
        ComponentOperation component = new ConcreteComponent();
        Runnable additionalBehavior = () -> System.out.println("Executing additional behavior");
        ComponentOperation decoratedComponent = new Decorator(component, additionalBehavior);
        decoratedComponent.execute();
    }
}

在这个例子中,我们没有使用继承或抽象类,而是使用了组合和Java 8的函数式接口来实现装饰器模式。这种方式更加灵活,并且允许在运行时动态地添加不同的行为。

总结

装饰器模式是一种强大的设计工具,它允许开发人员在不修改现有类的情况下动态地扩展对象的行为。通过合理地使用装饰器模式,可以构建出更加灵活、可扩展和可维护的软件系统。


相关文章
|
2月前
|
设计模式 Java Spring
Java 设计模式之责任链模式:优雅处理请求的艺术
责任链模式通过构建处理者链,使请求沿链传递直至被处理,实现发送者与接收者的解耦。适用于审批流程、日志处理等多级处理场景,提升系统灵活性与可扩展性。
258 2
|
2月前
|
设计模式 网络协议 数据可视化
Java 设计模式之状态模式:让对象的行为随状态优雅变化
状态模式通过封装对象的状态,使行为随状态变化而改变。以订单为例,将待支付、已支付等状态独立成类,消除冗长条件判断,提升代码可维护性与扩展性,适用于状态多、转换复杂的场景。
292 0
|
4月前
|
设计模式 缓存 Java
Java设计模式(二):观察者模式与装饰器模式
本文深入讲解观察者模式与装饰器模式的核心概念及实现方式,涵盖从基础理论到实战应用的全面内容。观察者模式实现对象间松耦合通信,适用于事件通知机制;装饰器模式通过组合方式动态扩展对象功能,避免子类爆炸。文章通过Java示例展示两者在GUI、IO流、Web中间件等场景的应用,并提供常见陷阱与面试高频问题解析,助你写出灵活、可维护的代码。
|
2月前
|
设计模式 算法 搜索推荐
Java 设计模式之策略模式:灵活切换算法的艺术
策略模式通过封装不同算法并实现灵活切换,将算法与使用解耦。以支付为例,微信、支付宝等支付方式作为独立策略,购物车根据选择调用对应支付逻辑,提升代码可维护性与扩展性,避免冗长条件判断,符合开闭原则。
289 35
|
2月前
|
设计模式 消息中间件 传感器
Java 设计模式之观察者模式:构建松耦合的事件响应系统
观察者模式是Java中常用的行为型设计模式,用于构建松耦合的事件响应系统。当一个对象状态改变时,所有依赖它的观察者将自动收到通知并更新。该模式通过抽象耦合实现发布-订阅机制,广泛应用于GUI事件处理、消息通知、数据监控等场景,具有良好的可扩展性和维护性。
249 8
|
7月前
|
设计模式 缓存 安全
【高薪程序员必看】万字长文拆解Java并发编程!(8):设计模式-享元模式设计指南
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的经典对象复用设计模式-享元模式,废话不多说让我们直接开始。
170 0
|
4月前
|
设计模式 安全 Java
Java设计模式(一):单例模式与工厂模式
本文详解单例模式与工厂模式的核心实现及应用,涵盖饿汉式、懒汉式、双重检查锁、工厂方法、抽象工厂等设计模式,并结合数据库连接池与支付系统实战案例,助你掌握设计模式精髓,提升代码专业性与可维护性。
|
4月前
|
设计模式 XML 安全
Java枚举(Enum)与设计模式应用
Java枚举不仅是类型安全的常量,还具备面向对象能力,可添加属性与方法,实现接口。通过枚举能优雅实现单例、策略、状态等设计模式,具备线程安全、序列化安全等特性,是编写高效、安全代码的利器。
|
设计模式 XML Java
【设计模式】装饰器模式(定义 | 特点 | Demo入门讲解)
【设计模式】装饰器模式(定义 | 特点 | Demo入门讲解)
135 0
|
9月前
|
设计模式 Java 数据安全/隐私保护
Java 设计模式:装饰者模式(Decorator Pattern)
装饰者模式属于结构型设计模式,允许通过动态包装对象的方式为对象添加新功能,提供比继承更灵活的扩展方式。该模式通过组合替代继承,遵循开闭原则(对扩展开放,对修改关闭)。

热门文章

最新文章