对象缓存技术:避免重复调用耗时方法的实现方案
以下是几种避免重复调用longRunningMethod的方案,同时解决直接传int参数含义不明确的问题:
方案1:在C类内部添加结果缓存
直接在C类中对longRunningMethod的结果进行缓存,确保方法仅执行一次,后续调用直接返回缓存值。适合该方法的结果在C实例生命周期内不会变化的场景。
public class C { private Integer cachedResult; // 用Integer区分未初始化和结果为0的情况 public int longRunningMethod() { if (cachedResult == null) { // 执行原耗时逻辑 cachedResult = doLongRunningCalculation(); } return cachedResult; } // 把原耗时逻辑抽离,让缓存逻辑更清晰 private int doLongRunningCalculation() { ... } }
说明:无需修改A、B和MyProgram的代码(仅需修正B类构造函数的笔误,将public A(C c)改为public B(C c)),调用逻辑完全不变,但longRunningMethod只会执行一次。如果后续需要刷新结果,可以新增resetCache()方法清除缓存。
方案2:封装结果为有意义的业务对象
将longRunningMethod的结果封装成一个具有明确业务含义的对象,替代直接传递int,既解决参数含义模糊的问题,又能确保结果只计算一次。
// 根据实际业务场景命名,比如"TaskExecutionResult"、"CalculatedMetric" public class CalculatedTaskResult { private final int value; public CalculatedTaskResult(int value) { this.value = value; } public int getValue() { return value; } } // 修改A类,依赖业务对象而非C public class A { private final CalculatedTaskResult result; public A(CalculatedTaskResult result) { this.result = result; } public void method1() { int value = result.getValue(); // 原业务逻辑 ... } } // 修改B类,同理 public class B { private final CalculatedTaskResult result; public B(CalculatedTaskResult result) { this.result = result; } public void method2() { int value = result.getValue(); // 原业务逻辑 ... } } // MyProgram中仅调用一次耗时方法 public class MyProgram { public static void main(String[] args) { C c = new C(); CalculatedTaskResult result = new CalculatedTaskResult(c.longRunningMethod()); A a = new A(result); B b = new B(result); a.method1(); b.method2(); // 无需再调用C的耗时方法 } }
说明:通过业务对象明确参数含义,同时将计算逻辑集中在入口处,避免重复调用。如果A、B仍需要使用C的其他方法,可以同时注入C和CalculatedTaskResult。
方案3:使用代理类缓存结果
给C定义接口,创建代理类封装缓存逻辑,A、B依赖接口而非具体实现,通过代理确保方法仅执行一次。这种方式符合依赖倒置原则,扩展性更好。
// 定义C的接口,抽象核心方法 public interface CService { int longRunningMethod(); } // 原C类实现接口 public class C implements CService { @Override public int longRunningMethod() { // 原耗时逻辑 ... } } // 代理类,专门处理缓存逻辑 public class CServiceProxy implements CService { private final CService target; private Integer cachedResult; public CServiceProxy(CService target) { this.target = target; } @Override public int longRunningMethod() { if (cachedResult == null) { cachedResult = target.longRunningMethod(); } return cachedResult; } } // MyProgram中注入代理对象 public class MyProgram { public static void main(String[] args) { CService c = new CServiceProxy(new C()); A a = new A(c); B b = new B(c); a.method1(); // 首次调用执行耗时逻辑 b.method2(); // 后续调用直接返回缓存结果 } }
说明:代理类隔离了缓存逻辑,原C类无需修改,A、B的代码也无需变动。如果后续需要更换缓存策略(比如超时自动刷新),只需修改代理类即可。
方案4:让A和B共享结果持有者
创建一个结果持有者类,负责管理C的计算结果,A、B依赖该持有者,由持有者确保仅调用一次longRunningMethod。
public class CResultHolder { private final C c; private Integer cachedResult; public CResultHolder(C c) { this.c = c; } public int getLongRunningResult() { if (cachedResult == null) { cachedResult = c.longRunningMethod(); } return cachedResult; } // 如果需要使用C的其他方法,可以暴露getter public C getC() { return c; } } // 修改A类 public class A { private final CResultHolder resultHolder; public A(CResultHolder resultHolder) { this.resultHolder = resultHolder; } public void method1() { int value = resultHolder.getLongRunningResult(); // 原业务逻辑 ... } } // 修改B类同理 public class B { private final CResultHolder resultHolder; public B(CResultHolder resultHolder) { this.resultHolder = resultHolder; } public void method2() { int value = resultHolder.getLongRunningResult(); // 原业务逻辑 ... } } // MyProgram中初始化持有者 public class MyProgram { public static void main(String[] args) { C c = new C(); CResultHolder holder = new CResultHolder(c); A a = new A(holder); B b = new B(holder); a.method1(); b.method2(); } }
说明:持有者类专门负责结果的缓存和获取,A、B无需关心计算逻辑,只需从持有者拿结果。如果A、B还需要使用C的其他方法,可以通过持有者的getC()方法获取实例。
内容的提问来源于stack exchange,提问作者peer

