Bootstrap

动态代理与通用装饰器的完美结合

在软件开发中,装饰器模式是一种常用的设计模式,用于在不修改原有对象的基础上增加新的功能。传统的装饰器模式需要为每个接口编写对应的装饰器类,这在面对多个接口时会变得繁琐。今天,我们将探讨如何利用JDK的动态代理来实现一个通用的装饰器,从而简化代码并提高复用性。

传统装饰器模式的局限

首先,我们来看一个简单的例子,假设我们有一个接口 IObject 和一个实现类 MyObject

public interface IObject {
    String getData();
}

public class MyObject implements IObject {
    @Override
    public String getData() {
        return "expensiveData-" + System.nanoTime();
    }
}

如果我们想为 MyObject 添加缓存功能,可以使用传统的装饰器模式:

import java.util.HashMap;
import java.util.Map;

public class NormalCacheDecorator implements IObject {
    private IObject original;
    private Map<String, Object> cacheData = new HashMap<>();

    public NormalCacheDecorator(IObject original) {
        this.original = original;
    }

    @Override
    public String getData() {
        Object data = cacheData.get("getData");
        if (data == null) {
            data = original.getData();
            cacheData.put("getData", data);
        }
        return (String) data;
    }
}

使用装饰器:

public static void main(String[] args) {
    MyObject object = new MyObject();
    IObject decorator = new NormalCacheDecorator(object);
    System.out.println(decorator.getData());
    System.out.println(decorator.getData());
}

输出结果:

expensiveData-1781118984883837
expensiveData-1781118984883837

这种方法虽然有效,但当有多个接口需要缓存功能时,我们需要为每个接口编写一个装饰器类,这显然是不理想的.

动态代理的引入

JDK的动态代理允许我们在运行时动态地创建代理对象,从而避免了为每个接口编写装饰器类的麻烦。下面是一个使用动态代理实现的通用缓存装饰器:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class GenericCacheDecorator implements InvocationHandler {
    private Map<String, Object> cachedData = new HashMap<>();
    private Object EMPTY = new Object();
    private Object obj;

    public GenericCacheDecorator(Object obj) {
        this.obj = obj;
    }

    public static <I, T extends I> I decorate(T t, Class<I> interfaceClass) {
        GenericCacheDecorator cacheableDecorator = new GenericCacheDecorator(t);
        return (I) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                                          new Class[]{interfaceClass}, cacheableDecorator);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (cachedData.containsKey(method.getName())) {
            Object o = cachedData.get(method.getName());
            if (o == EMPTY) {
                Object returned = method.invoke(obj, args);
                cachedData.put(method.getName(), returned);
                return returned;
            } else {
                return o;
            }
        }
        return method.invoke(obj, args);
    }
}

使用动态代理装饰器:

public static void main(String[] args) {
    MyObject object = new MyObject();
    IObject iObject = GenericCacheDecorator.decorate(object, IObject.class);
    System.out.println(iObject.getData());
    System.out.println(iObject.getData());
}

输出结果:

expensiveData-1783122642531745
expensiveData-1783122642531745

总结

通过使用JDK的动态代理,我们可以实现一个通用的装饰器,从而避免为每个接口编写单独的装饰器类。这种方法不仅简化了代码,还提高了代码的复用性和可维护性。在实际开发中,我们可以根据需要灵活地扩展和应用这种模式,以满足不同的功能需求.

;