【问题标题】:How can I remove duplicated code between classes?如何删除类之间的重复代码?
【发布时间】:2018-10-31 17:17:25
【问题描述】:

我有 2 个类:RecursiveFibonacci 和 MemorizedRecursiveFibonacci。这就是我到目前为止所拥有的。

递归斐波那契类

public class SimpleRecursiveFibonacci {

  public BigInteger fibonacci(int n) {
    if(n < 2) {
      return BigInteger.ONE;             
    }

    return fibonacci(n - 2).add(fibonacci(n - 1));
  }
}

和 MemorizedRecursiveFibonacci 类

public class MemoizedRecursiveFibonacci {
  private Map<Integer, BigInteger> cache = new HashMap<>();

  public BigInteger fibonacci(int n) {
    if(n < 2) {
      return BigInteger.ONE;
    }
    if(!cache.containsKey(n)){
      BigInteger currentFibonacci = fibonacci(n - 2).add(fibonacci(n - 1));
      cache.put(n, currentFibonacci);
    }

    return cache.get(n);
  }
}

如我所见,MemorizedRecursiveFibonacci 类中有一些重复的代码

 if(n < 2) {
      return BigInteger.ONE;

  BigInteger currentFibonacci = fibonacci(n - 2).add(fibonacci(n - 1));

如何保持干燥?删除重复代码?

【问题讨论】:

  • 如何使用 MemoizedRecursiveFibonacci 并实现类似 public BigInteger fibonacci(int n, bool useCache) 的方法并让调用者使用缓存?或者使用属性@Cacheable 来装饰方法来维护缓存?
  • 一个不相关的观察:记忆化版本总是用连续的整数键填充 HashMap。因此,使用 ArrayList 并使用索引而不是键会更有效。

标签: java dry


【解决方案1】:

另一个使用 Java 8 功能的示例 - BiFunction 接口与 lambda 表达式:

BiFunction<Fibonacci, Integer, BigInteger> func = (fibonacci, n) -> {
    if (n < 2) {
        return BigInteger.ONE;
    }
    return fibonacci.calc(n - 2).add(fibonacci.calc(n - 1));
};

new CachedFibonacci(func).calc(100);

实施:

interface Fibonacci {
    BigInteger calc(int n);
}

class SimpleFibonacci implements Fibonacci {

    private BiFunction<Fibonacci, Integer, BigInteger> fibonacci;

    SimpleFibonacci(BiFunction<Fibonacci, Integer, BigInteger> fibonacci) {
        this.fibonacci = fibonacci;
    }

    public BigInteger calc(int n) {
        return fibonacci.apply(this, n);
    }
}

class CachedFibonacci implements Fibonacci {

    private BiFunction<Fibonacci, Integer, BigInteger> fibonacci;
    private Map<Integer, BigInteger> cache = new HashMap<>();

    CachedFibonacci(BiFunction<Fibonacci, Integer, BigInteger> fibonacci) {
        this.fibonacci = fibonacci;
    }

    public BigInteger calc(int n) {
        if (!cache.containsKey(n)) {
            cache.put(n, fibonacci.apply(this, n));
        }
        return cache.get(n);
    }
}

【讨论】:

  • 这行得通,它完成了它应该做的事情,但我发现它有点难以理解。它似乎以降低可读性来消除重复。
【解决方案2】:

也许这是一个选择......但不是我认为的最好的。

public class SimpleRecursiveFibonacci {

public BigInteger fibonacci(int n) {
    if(n < 2) {
        return BigInteger.ONE;
    }
    return calculate(n);
}

protected BigInteger calculate(int n){
    return fibonacci(n - 2).add(fibonacci(n - 1)),
}

}

public class MemoizedRecursiveFibonacci extends SimpleRecursiveFibonacci{
private Map<Integer, BigInteger> cache = new HashMap<>();

@Override
protected BigInteger calculate(int n) {
    if(!cache.containsKey(n)){
        BigInteger currentFibonacci = super.calculate(n);
        cache.put(n, currentFibonacci);
    }
    return cache.get(n)
}

}

【讨论】:

  • 其实还不错。经典模板方法 :) 我要做的唯一改进是尽早返回 currentFibonacci 而不是再次查找。
  • 类似于我想出的。我使用第三种方法来“获取”斐波那契值,无论是从缓存中还是通过重新计算。
【解决方案3】:

这样的事情怎么样:

public class SimpleRecursiveFibonacci {

    /** Gets the fibonacci value for n */
    public final BigInteger fibonacci(int n) {
        if (n == 0) {
            return BigInteger.ZERO;
        } else if (n == 1) {
            return BigInteger.ONE;
        }
        return getFibonacci(n);
    }

    /** Recursively calculates the fibonacci by adding the two previous fibonacci. */
    protected final BigInteger calculateFibbonacci(int n) {
        return fibonacci(n - 2).add(fibonacci(n - 1));
    }

    /** 
     * Somehow get the fibonacci value for n.
     * Could be by calculation, getting it from a cache, or anything.
     * Defaults to calculation.
     */
    protected BigInteger getFibonacci(int n) {
        return calculateFibbonacci(n);
    }

}

public class MemoizedRecursiveFibonacci extends SimpleRecursiveFibonacci {

    // Cache using an array list as recommended by user @DodgyCodeException
    private ArrayList<BigInteger> cache = new ArrayList<>();

    @Override
    protected BigInteger getFibonacci(int n) {
        if (cache.size() < n) {
            BigInteger fib = calculateFibbonacci(n);
            cache.add(fib);
            return fib;
        } else {
            return cache.get(n - 1);
        }
    }
}

【讨论】:

    【解决方案4】:

    使用静态方法:

    public class SimpleRecursiveFibonacci {
    
      public static BigInteger fibonacci(int n) {
        if(n < 2) {
          return BigInteger.ONE;             
        }
    
        return fibonacci(n - 2).add(fibonacci(n - 1));
      }
    }
    
    public class MemoizedRecursiveFibonacci {
      private Map<Integer, BigInteger> cache = new HashMap<>();
    
      public BigInteger fibonacci(int n) {
        if(!cache.containsKey(n)){
          BigInteger currentFibonacci = SimpleRecursiveFibonacci.fibonacci(n);
          cache.put(n, currentFibonacci);
        }
        return cache.get(n);
      }
    }
    

    【讨论】:

    • 这只会记住最终值,而不是中间值。
    猜你喜欢
    • 2017-11-22
    • 1970-01-01
    • 1970-01-01
    • 2010-09-12
    • 2013-11-03
    • 1970-01-01
    • 2021-09-11
    • 1970-01-01
    相关资源
    最近更新 更多