在软件开发中,装饰器模式是一种常用的设计模式,用于在不修改原有对象的基础上增加新的功能。传统的装饰器模式需要为每个接口编写对应的装饰器类,这在面对多个接口时会变得繁琐。今天,我们将探讨如何利用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的动态代理,我们可以实现一个通用的装饰器,从而避免为每个接口编写单独的装饰器类。这种方法不仅简化了代码,还提高了代码的复用性和可维护性。在实际开发中,我们可以根据需要灵活地扩展和应用这种模式,以满足不同的功能需求.