Java装饰器模式能否访问辅助方法?场景实现疑问
问题描述
现有一个旗帜接口flag,包含wave()和辅助方法calculateWind()。实现类flagImpl的wave()会调用自身的calculateWind();装饰类decoratedFlag对两个方法都做了装饰,但执行代码后仅输出this is decorated,装饰后的calculateWind()从未被触发。
需要解决:能否让flagImpl调用已实例化的装饰器对象的calculateWind()而非自身的?或者装饰器模式下有没有通用解决思路?
代码示例(修正原代码递归错误):
public class Main { public static void main(String[] args) { flag flag = new decoratedFlag(new flagImpl()); flag.wave(); } } public interface flag { int wave(); int calculateWind(); } public class flagImpl implements flag { @Override public int wave() { return calculateWind(); } @Override public int calculateWind() { return 8; } } public class decoratedFlag implements flag { private flag flag; public decoratedFlag(flag flag) { this.flag = flag; } @Override public int wave() { System.out.println("this is decorated"); return flag.wave(); } @Override public int calculateWind() { System.out.println("this is ALSO decorated"); return flag.calculateWind(); } }
解决思路与方案
核心问题是:flagImpl的wave()直接调用自身calculateWind()时,this指向自身而非外层装饰器,导致装饰逻辑无法介入。以下是两种可行方案:
方案1:重构实现类,注入方法依赖
修改flagImpl,让它不再硬编码调用自身方法,而是通过构造函数传入一个flag类型的依赖,让装饰器可以把自身注入进去,从而让flagImpl调用装饰器的calculateWind()。
示例代码:
public class flagImpl implements flag { private flag windCalculator; // 无参构造保持兼容,默认使用自身逻辑 public flagImpl() { this.windCalculator = this; } // 带参构造,允许注入外部的方法提供者 public flagImpl(flag windCalculator) { this.windCalculator = windCalculator; } @Override public int wave() { return windCalculator.calculateWind(); // 调用注入对象的方法 } @Override public int calculateWind() { return 8; } } // 装饰器使用时,将自身传入flagImpl public class Main { public static void main(String[] args) { decoratedFlag decorator = new decoratedFlag(null); flag flag = new decoratedFlag(new flagImpl(decorator)); flag.wave(); } } public class decoratedFlag implements flag { private flag flag; public decoratedFlag(flag flag) { this.flag = flag; } @Override public int wave() { System.out.println("this is decorated"); return flag.wave(); } @Override public int calculateWind() { System.out.println("this is ALSO decorated"); return 8; // 可根据需求选择是否调用原对象方法 } }
方案2:动态代理(通用装饰方案)
如果不想修改原有flagImpl代码,可使用动态代理拦截所有接口方法调用——包括实现类内部的方法调用,确保所有通过接口的操作都经过装饰逻辑。
示例代码(基于JDK动态代理):
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class DynamicFlagDecorator implements InvocationHandler { private flag target; private DynamicFlagDecorator(flag target) { this.target = target; } public static flag decorate(flag target) { return (flag) Proxy.newProxyInstance( target.getClass().getClassLoader(), new Class[]{flag.class}, new DynamicFlagDecorator(target) ); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 执行装饰逻辑 if (method.getName().equals("wave")) { System.out.println("this is decorated"); } else if (method.getName().equals("calculateWind")) { System.out.println("this is ALSO decorated"); } // 调用目标方法,确保内部接口方法调用走代理 return method.invoke(target, args); } } // 使用方式 public class Main { public static void main(String[] args) { flag flag = DynamicFlagDecorator.decorate(new flagImpl()); flag.wave(); } }
这种方式无需修改原有实现类,只要接口定义了所有需要装饰的方法,就能覆盖所有调用场景。
装饰器模式通用注意点
- 默认情况下,被装饰对象内部的方法调用不会经过装饰器,因为
this指向自身而非装饰器; - 要让内部调用被装饰,要么通过依赖注入转移方法调用控制权,要么用动态代理拦截所有接口方法;
- 确保所有需要装饰的方法都定义在接口中,否则非接口方法的内部调用无法被拦截。
内容的提问来源于stack exchange,提问作者Rasmus Hjorth Lüdeking
相关产品推荐
相关产品推荐

