Java 如何避免耦合两种方法,这两种方法现在具有类似的实现,但将来可能会发生变化?

Java 如何避免耦合两种方法,这两种方法现在具有类似的实现,但将来可能会发生变化?,java,Java,我在一个类中有两个方法,它们目前共享一个非常相似的实现,但是这个实现在性能方面非常昂贵 样本: class Example { public void process(Object obj) { boolean someFact = getFirstFact(obj); boolean someOtherFact = getSecondFact(obj); //use the two facts somehow }

我在一个类中有两个方法,它们目前共享一个非常相似的实现,但是这个实现在性能方面非常昂贵

样本:

class Example
{
    public void process(Object obj)
    {
        boolean someFact = getFirstFact(obj);
        boolean someOtherFact = getSecondFact(obj);

        //use the two facts somehow
    }

    public boolean getFirstFact(Object obj)
    {
         boolean data = someExpensiveMethod(obj);
         //return some value derived from data
    }

    public boolean getSecondFact(Object obj)
    {
         boolean data = someExpensiveMethod(obj);
         //return some other value derived from data
    }

    public boolean someExpensiveMethod(Object obj){...}
}
我曾想过以某种方式缓存某个ExpensiveMethod的结果,但这似乎是浪费,因为对象往往会进入、处理然后丢弃。它似乎也很笨拙——方法需要知道缓存,或者我需要在SSOMeeExpensiveMethod中缓存结果

即使是短期缓存也可能是坏消息,因为每天都有数百万个对象被处理


我的担忧有两个方面:第一,不能保证这两种方法始终依赖于第三种方法,因此任何解决方案都应该从其POV中透明,第二,显而易见的解决方案(在某些昂贵的方法中隐藏),就不需要长期保存的结果的空间而言,可能非常昂贵。

您是否总是调用流程方法(我的意思是,您从不直接调用get…Fact方法)?如果是这种情况,那么您肯定知道getFirstFact总是在getSecondFact之前被调用

然后,您可以使用私有字段在getFirstFact方法中缓存someExpensiveMethod的布尔输出,并在getSecondFact方法中重用该值:

class Example
{
    private boolean _expensiveMethodOutput;

    public void process(Object obj)
    {
        boolean someFact = getFirstFact(obj);
        boolean someOtherFact = getSecondFact(obj);

        //use the two facts somehow
    }

    private boolean getFirstFact(Object obj)
    {
         _expensiveMethodOutput = someExpensiveMethod(obj);
         //return some value derived from data
    }

    private boolean getSecondFact(Object obj)
    {
         boolean data = _expensiveMethodOutput;
         //return some other value derived from data
    }

    private boolean someExpensiveMethod(Object obj){...}
}

从你的题目来看,我猜你不想这样做

class Example
{
    public void process(Object obj)
    {
        boolean expensiveResult = someExpensiveMethod(obj);
        boolean someFact = getFirstFact(expensiveResult);
        boolean someOtherFact = getSecondFact(expensiveResult);

        //use the two facts somehow
    }
    ...
因为这意味着在更改其中一种方法时,您将无法再访问
obj
。此外,您希望尽可能避免执行昂贵的方法。一个简单的解决办法是

private Object lastParam = null;
private boolean lastResult = false;
public boolean someExpensiveMethod(Object obj){
    if (obj == lastParam) return lastResult;
    lastResult = actualExpensiveMethod(obj);
    lastParam = obj;
    return lastResult ;
}

当然,这对多线程不起作用。(至少确保<代码>进程>代码>同步。)

< p>我会考虑引入一个工厂方法和一个封装预处理的新对象。这样,一旦对象超出范围,jvm就可以丢弃预处理的数据

class PreprocessedObject {
    private ... data;

    public static PreprocessedObject  create(Object obj) {
        PreprocessedObject pObj = new PreprocessedObject();
        // do expensive stuff
        pObj.data = ...
        return pObj;
    }

    public boolean getFirstFact() {
         //return some value derived from data
    }

    public boolean getSecondFact() {
         //return some other value derived from data
    }
}
我曾考虑过以某种方式缓存
someExpensiveMethod
的结果,但这似乎是浪费,因为对象往往会进入、处理然后丢弃

我不认为这是浪费。缓存基本上就是这样工作的。您将进入的对象与最近处理的对象进行比较,当您得到“命中”时,您可以避免调用
someExpensiveMethod
的开销

缓存是否真正适用于您的应用程序将取决于许多因素,如:

  • 可以保留在缓存中的对象/结果对的数量
  • “命中”的概率
  • 执行缓存探测的平均成本(在“命中”和“未命中”情况下)
  • 调用
    someExpensiveMethod
  • 维护缓存的直接成本;e、 g.如果您使用LRU或其他一些策略来清除没有帮助的缓存项,以及
  • 维护缓存的间接成本
(最后一点很难预测/衡量,但它包括表示缓存结构所需的额外内存、GC为处理缓存及其内容“可访问”这一事实而必须做的工作,以及与弱引用相关的GC开销……假设您使用它们。)

最终,缓存解决方案的成功(或不成功)是根据系统对实际工作负载的平均行为来判断的。一些缓存结果不再被使用的事实与此无关

它似乎也很笨拙——方法需要知道缓存,或者我需要在
someExpensiveMethod
中缓存结果

在我看来,这两种方式都不“笨重”。这是实现缓存的方式

即使是短期缓存也可能是坏消息,因为每天都有数百万个对象被处理

再说一遍,我看不出你论点的逻辑。如果每天处理数以百万计的对象,而你保持(比如)最后5分钟的价值,那么只需要缓存数万个对象。这绝非“坏消息”

如果您真的每天处理“数以百万计”的对象,那么:

  • 一些昂贵的方法不可能那么昂贵。。。除非您拥有高效缓存和大量内存,或者拥有大量处理器,或者两者兼而有之
  • 您对优雅(不简洁)和避免耦合的关注必须是设计应用程序以使其跟上的次要问题,并且
  • 您可能需要在多处理器上运行,因此需要处理缓存可能成为并发瓶颈的事实

    • 除了斯蒂芬的答案,我建议你看看谷歌番石榴。有一个计算地图的概念,适合您在这里面临的问题。我写了一篇关于这方面的文章

      就代码而言,我建议如下:

      class Example {
      
          private ConcurrentMap<Object, Boolean> cache;
      
          void initCache() {
              cache = new MapMaker().softValues()
                          .makeComputingMap(new Function<Object, Boolean>() {
      
                  @Override
                  public Boolean apply(Object from) {
                      return someExpensiveMethod(from);
                  }
              });
          }
      
          public void process(Object obj) {
              boolean someFact = getFirstFact(obj);
              boolean someOtherFact = getSecondFact(obj);
      
              // use the two facts somehow
          }
      
          public boolean getFirstFact(Object obj) {
              boolean data = cache.get(obj);
              // return some value derived from data
          }
      
          public boolean getSecondFact(Object obj) {
              boolean data = cache.get(obj);
              // return some other value derived from data
          }
      
          public boolean someExpensiveMethod(Object obj) {
          }
      }
      
      类示例{
      私有ConcurrentMap缓存;
      void initCache(){
      cache=新的MapMaker().softValues()
      .makeComputingMap(新函数(){
      @凌驾
      公共布尔应用(对象来自){
      返回一些ExpensiveMethod(从);
      }
      });
      }
      公共作废流程(对象obj){
      布尔someFact=getFirstFact(obj);
      布尔someOtherFact=getSecondFact(obj);
      //以某种方式使用这两个事实
      }
      公共布尔getFirstFact(对象obj){
      布尔数据=cache.get(obj);
      //返回从数据中派生的一些值
      }
      公共布尔getSecondFact(对象obj){
      布尔数据=cache.get(obj);
      //返回从数据派生的其他值
      }
      公共布尔someExpensiveMethod(对象obj){
      }
      }
      
      标题与问题不符-您是否担心耦合问题,还是希望找到一种缓存结果的好方法