重构一个简单填充的长方法

时间:2010-06-02 10:12:29

标签: java refactoring

我正在重构一个超过500行的方法(不要问我原因)

该方法基本上从数据库中查询地图列表,并且对于列表中的每个地图进行一些计算,并将该计算的值添加到地图中。然而,有太多的计算和完成,代码已经达到500多行!

以下是示例预览:

public List<Hashmap> getProductData(...) {
   List<Hashmap> products = productsDao.getProductData(...);
   for (Product product: products) {
       product.put("Volume",new BigDecimanl(product.get("Height")*
              product.get("Width")*product.get("Length"));
   //over 10 more lines like the one above
      if (some condition here) {
         //20 lines worth of product.put(..,..) 
      } else {
         //20 lines worth of product.put(..,..)
      }
      //3 more if-else statements like the one above
      try {
         product.put(..,..)
      } catch (Exception e) {
         product.put("",..)
      }
      //over 8 more try-catches of the form above
   }

关于如何重构这个的任何想法?

5 个答案:

答案 0 :(得分:7)

我想出一个简单的想法来划分一个方法是“找出有意义的小任务”,所以这里有一些提示:

为商品级

制作方法
processCollection(collection) {
// startup code
for (Item i: collection)
  processCollectionItem(i, ...other args...);
// final code
}

尝试按照自己的方法剪切每个评论区块

// does a
instr 1
instr 2
instr 3
instr 4
// does b
instr 5
instr 6
instr 7
instr 8

转换为

a(...);
b(...);

试着看看是否有一些行是某种一般模式的具体表达

map.put(a[0], b[0]);
map.put(a[1], b[1]);
...

转换为

putKeysAndValues(a, b);

putKeysAndValues(a, b) {
  for (int i=0; i<a.length; i++)
     map.put(a[i], b[i]);
}

答案 1 :(得分:4)

这取决于业务逻辑,但最直接的方法是将long方法拆分为较小的方法:

for (Product product: products) {
   handleProduct(product);
}


private void handleProduct(Product product) throws Exception {
  if (condition1) {
     handleProduct1(product);
  }
}

如果每个条件与特定业务案例匹配并且名称有意义,那么这将更有意义。

答案 2 :(得分:2)

如果没有更多信息,很难提供详细的答案,但您可以考虑的一种方法是:

  • 使用Product类层次结构重新构建产品,而不是将每个产品表示为Map(名称,值)对。当前的Map表示意味着在运行时才会检测到许多错误;最好选择更强的打字,这样你就可以在编译时执行更多的检查。
  • 在顶级Product类或接口上定义模板方法,该方法执行当前在500行方法中定义的大部分处理。例如,您可以根据以下代码提取将访问者传递给模板方法。

示例

/**
 * Our visitor definition, responsible for processing each product.
 */
public interface ProductVisitor {
  void processProductA(ConcreteProductA a);

  void processProductB(ConcreteProductB b);
}

/**
 * Top-level product definition.
 */
public interface Product {
  void process(ProductVisitor v);
}

/**
 * A conrete product implementation.  Makes a callback to the visitor
 * allowing itself to be processed.
 */
public class ConcreteProductA implements Product {
  public void process(ProductVisitor v) {
    v.processProductA(this);
  }
}

/**
 * As per the previous product implementation but makes a *different*
 * callback to the visitor.
 */
public class ConcreteProductB implements Product {
  public void process(ProductVisitor v) {
    v.processProductB(this);
  }
}

这种方法的主要优点是可以避免在代码中使用大量明确的if-then块,从而使设计更加松散耦合。缺点是它可能使代码的可读性降低。

答案 3 :(得分:1)

假设你无法改变道具...这就是我接近它的方式,不一定正确:

public List<Hashmap> getProductData(...) {
   List<Hashmap> products = productsDao.getProductData(...);
   try{
     addVolume(products);
     ...
     doCondition1(products);
     ...
   } catch (E1 e){
     ...
   } ... {
   }
}

public addVolume(List<HashMap> products) throws E1, E2, ... {
  for(HashMap product : products){
    product.put("Volume",new BigDecimanl(product.get("Height")*
              product.get("Width")*product.get("Length"));
  }
}

... do other things ...

public doCondition1(List<HashMap> products) throws E1, E2, ...{
  for(HashMap product : products){
    if(condition1){
      ...
    }else{
      ...
    }
  }
}

...condition 2, condition 3, ...

当然,您可以将循环放在

  

getProductData(...)

答案 4 :(得分:0)

您可以通过将条件转移移动到单独的方法并从当前方法调用它来缩短方法。

例如

...
if (some condition here) 
 { method1(map);} 
  else 
{method2(map);}

此外,如果有共同的任务,也可以采用不同的方法。