删除多个if-else的最佳方法

时间:2016-07-10 12:45:26

标签: java oop design-patterns

所以相似类型的问题在其他地方得到解答,但在这里我期待在给定情况下省略if else链的最佳方法。

当前代码

private ViewModel getViewModel(Receipt receipt) {
  String receiptType = receipt.type;

  if(receiptType.equals("HOTEL")) {
    return new HotelReceiptViewModel(receipt));
  } else if(receiptType.equals("CAR")) {
    return new CarReceiptViewModel(receipt));
  }
  .
  .
  .
  } else if(receiptType.equals("LUNCH")) {
    return new FoodReceiptViewModel(receipt));
  }
}

其中所有视图模型都扩展了一个名为ReceiptViewModel的类。 e.g。

public class HotelReceiptViewModel extends ReceiptViewModel implements ViewModel {
    public HotelReceiptViewModel(Receipt receipt) {
        super(receipt);
        this.receiptNumber = receipt.getDocumentNumber();
        this.receiptHeading = "HOTEL";
    }
}

目前有5种类型的收据,将来还会有3-4种收据。

可能的解决方案

  1. 使用HashMap
  2. 使用Enum
  3. 使用策略模式或命令模式
  4. 反思的使用
  5. 让我们看看每种方法的优缺点

    1。使用HashMap

    private ReceiptViewModel getViewModel(Receipt receipt) {
      Map<String, ReceiptViewModel> map = getViewModelsMap();
    
      String receiptType = receipt.type;
      ReceiptViewModel viewModel = map.get(receiptType);
      if(viewModel != null) {
        viewModel.setReceipt(receipt);
      }
    
      return viewModel;
    }
    
    private Map<String, ReceiptViewModel> getViewModelsMap() {
      Map<String, ReceiptViewModel> map = new HashMap<String, ReceiptViewModel>();
      map.add("HOTEL"), new HotelReceiptViewModel());
      map.add("CAR"), new CarReceiptViewModel());
      map.add("LUNCH"), new FoodReceiptViewModel());
    }
    

    ReceiptViewModel类看起来像

    public class HotelReceiptViewModel extends ReceiptViewModel implements ViewModel {
      public HotelReceiptViewModel(Receipt receipt) {
        super(receipt);
        this.receiptNumber = receipt.getDocumentNumber();
        this.receiptHeading = "HOTEL";
      }
    }
    

    赞成 更快,更容易,更可扩展。

    CONS ReceiptViewModel对象在构造函数中不需要Receipt类型的对象。使用setter设置Receipt,其中初始化ReceiptViewModel类的所有逻辑现在都将移动。

    2。使用枚举

    private ReceiptViewModel getViewModel(Receipt receipt) {
      String receiptType = receipt.type;
      ReceiptViewModel viewModel = 
            ReceiptViewModels.valueOf(receiptType).getReceiptViewModel(receipt);
    
      return viewModel;
    }
    

    Enum看起来像

    public enum ReceiptViewModels {
       HOTEL(
           ReceiptViewModel getReceiptViewModel(Receipt receipt) {
               return new HotelReceiptViewModel(receipt);
           }
        ),
       CAR(
           ReceiptViewModel getReceiptViewModel(Receipt receipt) {
               return new CarReceiptViewModel(receipt);
           }
        ),
        .
        .
        .
       LUNCH(
           ReceiptViewModel getReceiptViewModel(Receipt receipt) {
               return new FoodReceiptViewModel(receipt);
           }
        ),
    
        public abstract ReceiptViewModel getReceiptViewModel(Receipt receipt);
     }
    

    赞成 快,可能很容易。

    CONS 随着收据类型的增加,Enum的大小将继续增加,导致代码不可维护。

    ReceiptViewModels.valueOf(receiptType)需要已知的收据类型。如果新收据类型作为来自服务器的响应,则会生成IllegalArgumentException

    第3。使用反思

    Class<? extends ReceiptViewModel> viewModel = Class.
                forName(receiptType + name + "ReceiptViewModel").asSubclass(ReceiptViewModel.class);
            ReceiptViewModel receiptViewModel = viewModel .newInstance();
    

    CONS 1.较慢

    1. 当班级名称不同时,不能使用。例如对于LUNCH类型,视图模型类名称为FoodReceiptViewModel

    2. 从收据中获取值的逻辑被移动到setter而不是构造函数,就像HashMap一样

    3. 4。使用Strategy PatternTemplate Pattern

      PROS 易于理解,比反思更快

      CONS 可能是一种矫枉过正。将为每种收据添加一个新类。

      考虑以上所有要点,这是我的用例删除多个if-else块的最佳方法?

3 个答案:

答案 0 :(得分:2)

我会使用开关,除非有理由使用更复杂的东西。

private ViewModel getViewModel(Receipt receipt) {
  switch(receipt.type) {
    case "HOTEL": return new HotelReceiptViewModel(receipt);
    case "CAR": return new CarReceiptViewModel(receipt);
    case "LUNCH": return new FoodReceiptViewModel(receipt);
    default:
        throw new IllegalArgumentException("Unknown receipt type " + receipt.type);
}

我认为这是最好的解决方案,因为它是最简单的,满足您的需求。

答案 1 :(得分:1)

不需要任何行为,不需要任何结构,也不需要多态,所以我没有兴趣使用策略等设计模式,或者使用一个只会带来复杂性的地图。 你想要一个非常简单的状态机。所以,“if else if”或“switch”是完美的。 也许在主题之外但是使用私有方法,代码不是单一可测试的。

答案 2 :(得分:0)

我会在你的Receipt课程中创建一个新的抽象方法。像

这样的东西
public <T extends ReceiptViewModel> T createModel()

然后创建Receipt类的子类,其中每个type对应一个子类。然后每个子类将实现自己的createModel版本,返回ReceiptViewModel的正确子类。您的初始代码将像

一样简单
private ViewModel getViewModel(Receipt receipt) {
  return receipt.createModel()
}