• 作者:老汪软件技巧
  • 发表时间:2024-09-30 17:02
  • 浏览量:

定义

装饰模式(Decorator Pattern)是一种结构型设计模式,它允许你在不改变原有对象结构的情况下,动态地给对象添加新的功能。装饰模式通过创建一个装饰类,包裹原有的对象,并在装饰类中实现新的功能,同时调用被装饰对象的方法来保持原有行为。

以下是装饰模式的主要特点和定义:

一、主要角色

Component(抽象构件) :

ConcreteComponent(具体构件) :

Decorator(抽象装饰者) :

ConcreteDecorator(具体装饰者) :

业务类图

classDiagram
    class Beverage {
        <>
        +description : String
        +cost() : double
        +getDescription() : String
    }
    class Espresso {
        +Espresso()
        +cost() : double
        +getDescription() : String
    }
    class CondimentDecorator {
        <>
        +beverage : Beverage
        +cost() : double
        +getDescription() : String
    }
    class Milk {
        +Milk(Beverage)
        +cost() : double
        +getDescription() : String
    }
    class Sugar {
        +Sugar(Beverage)
        +cost() : double
        +getDescription() : String
    }
    Espresso --|> Beverage
    Milk --|> CondimentDecorator
    Sugar --|> CondimentDecorator
    Beverage <|-- CondimentDecorator

代码

以下是用 Java 实现装饰器模式的示例代码。假设我们有一个饮料的场景,有基本的咖啡和可以添加各种调料(如牛奶、糖等)的装饰器。

首先定义抽象构件Beverage (饮料):

public abstract class Beverage {
    public String description = "Unknown Beverage";
    public abstract double cost();
    public String getDescription() {
        return description;
    }
}

然后实现具体构件Espresso(浓缩咖啡):

public class Espresso extends Beverage {
    public Espresso() {
        description = "Espresso";
    }
    @Override
    public double cost() {
        return 1.99;
    }
}

接着定义抽象装饰者CondimentDecorator:

public abstract class CondimentDecorator extends Beverage {
    public abstract String getDescription();
}

再实现具体装饰者Milk(牛奶):

public class Milk extends CondimentDecorator {
    private Beverage beverage;
    public Milk(Beverage beverage) {
        this.beverage = beverage;
    }
    @Override
    public double cost() {
        return beverage.cost() + 0.5;
    }
    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Milk";
    }
}

以及另一个具体装饰者Sugar(糖):

public class Sugar extends CondimentDecorator {
    private Beverage beverage;
    public Sugar(Beverage beverage) {
        this.beverage = beverage;
    }
    @Override
    public double cost() {
        return beverage.cost() + 0.2;
    }
    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Sugar";
    }
}

最后使用这些类:

public class Main {
    public static void main(String[] args) {
        Beverage espresso = new Espresso();
        System.out.println(espresso.getDescription() + " $" + espresso.cost());
        Beverage espressoWithMilk = new Milk(espresso);
        System.out.println(espressoWithMilk.getDescription() + " $" + espressoWithMilk.cost());
        Beverage espressoWithMilkAndSugar = new Sugar(espressoWithMilk);
        System.out.println(espressoWithMilkAndSugar.getDescription() + " $" + espressoWithMilkAndSugar.cost());
    }
}

在这个示例中,Beverage是抽象构件,定义了饮料的基本接口。Espresso是具体构件,代表基本的浓缩咖啡。CondimentDecorator是抽象装饰者,Milk和Sugar是具体装饰者,它们在不改变原有对象结构的情况下,给饮料添加了新的功能(如添加牛奶或糖后的描述和价格变化)。

框架中的使用spring

在 Spring 框架中,装饰器模式有一些体现,比如在 Spring AOP(面向切面编程)中:

一、Spring AOP 的实现

装饰模式是结构模式吗__装饰模式实例

Spring AOP 通过创建代理对象来实现面向切面编程。在这个过程中,可以看作是对目标对象进行了装饰。

例如,当我们定义一个切面来对某个业务方法进行增强时,Spring 会在运行时创建一个代理对象,这个代理对象包裹着目标对象,并在调用目标方法前后执行切面中定义的逻辑(如日志记录、性能监控等)。

以下是一个简单的示例:

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.demo.service.*.*(..))")
    public void logBeforeMethodExecution(JoinPoint joinPoint) {
        System.out.println("Before method execution: " + joinPoint.getSignature().getName());
    }
    @AfterReturning(pointcut = "execution(* com.example.demo.service.*.*(..))", returning = "result")
    public void logAfterMethodExecution(JoinPoint joinPoint, Object result) {
        System.out.println("After method execution. Result: " + result);
    }
}

这里,LoggingAspect类就像是一个装饰器,它在不改变目标对象(被切面增强的业务类中的方法)结构的情况下,为目标方法添加了日志记录的功能。

二、自定义 BeanPostProcessor

Spring 允许我们实现自定义的BeanPostProcessor来对 Bean 进行额外的处理,这也可以看作是一种装饰器模式的应用。

例如,我们可以创建一个BeanPostProcessor来在 Bean 创建后对其进行一些特定的初始化或增强操作:

@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        // 在 Bean 初始化之前进行一些处理
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof SomeService) {
            SomeService service = (SomeService) bean;
            // 对 SomeService 类型的 Bean 进行特定的增强
            return new DecoratedService(service);
        }
        return bean;
    }
}

在这个例子中,CustomBeanPostProcessor可以看作是一个装饰器,它在 Bean 创建后根据特定的条件对 Bean 进行装饰(增强)。

综上所述,Spring 在多个方面体现了装饰器模式的思想,使得我们可以在不改变原有代码结构的情况下,动态地为对象添加新的功能。

mybatis

在 MyBatis 中,装饰器模式也有一定的体现。

例如,MyBatis 的缓存机制可以看作是一种装饰器模式的应用。MyBatis 提供了基础的缓存实现,同时也允许通过自定义缓存装饰器来增强缓存功能。

假设我们有一个自定义的二级缓存装饰器,它在原有的二级缓存基础上增加了一些额外的功能,如缓存统计、缓存过期时间控制等。

以下是一个简单的示例:

import org.apache.ibatis.cache.Cache;
import java.util.concurrent.locks.ReadWriteLock;
public class CustomCacheDecorator implements Cache {
    private final Cache delegate;
    public CustomCacheDecorator(Cache delegate) {
        this.delegate = delegate;
    }
    @Override
    public String getId() {
        return delegate.getId();
    }
    @Override
    public void putObject(Object key, Object value) {
        delegate.putObject(key, value);
    }
    @Override
    public Object getObject(Object key) {
        // 可以在这里添加额外的逻辑,如缓存统计
        return delegate.getObject(key);
    }
    @Override
    public Object removeObject(Object key) {
        return delegate.removeObject(key);
    }
    @Override
    public void clear() {
        delegate.clear();
    }
    @Override
    public int getSize() {
        return delegate.getSize();
    }
    @Override
    public ReadWriteLock getReadWriteLock() {
        return delegate.getReadWriteLock();
    }
}

在 MyBatis 的配置中,可以使用这个自定义的缓存装饰器来增强缓存功能:

<configuration>
    <settings>
        <setting name="cacheEnabled" value="true"/>
    settings>
    <plugins>
        <plugin interceptor="com.example.CustomCacheInterceptor">
            <property name="delegateCache" value="org.mybatis.caches.ehcache.EhcacheCache"/>
        plugin>
    plugins>
configuration>

这里的CustomCacheInterceptor可以在创建缓存对象时,使用自定义的装饰器来包裹原有的缓存实现,从而实现对缓存功能的增强。

通过这种方式,MyBatis 利用装饰器模式在不改变原有缓存结构的情况下,灵活地扩展了缓存的功能。

总结

使用场景

需要在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。当不能采用继承的方式来扩展对象的功能时,可以使用装饰模式。例如,当存在大量独立的扩展功能,而采用继承会导致大量的子类时。

优点

比继承更加灵活:可以在运行时动态地给对象添加功能,而继承是在编译时确定的。可以对一个对象进行多次装饰,添加不同的功能组合。符合开闭原则:可以在不修改原有代码的情况下,添加新的功能。

缺点

装饰模式会增加许多小类,使程序变得复杂。装饰模式的装饰顺序可能会影响最终的结果,需要仔细考虑装饰的顺序。