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

Raj*_*ran 5 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));
  }
}
Run Code Online (Sandbox Code Playgroud)

其中所有视图模型都扩展一个名为 的类ReceiptViewModel。例如

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

目前有5种收据类型,未来将增加3-4种收据类型。

可能的解决方案

  1. 用于HashMap
  2. 用于Enum
  3. 使用策略模式或命令模式
  4. 使用反射

让我们看看每种方法的优缺点


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());
}
Run Code Online (Sandbox Code Playgroud)

课程ReceiptViewModel看起来像

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

优点 更快、更简单、可扩展。

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


2. 枚举的使用

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

  return viewModel;
}
Run Code Online (Sandbox Code Playgroud)

枚举看起来像

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);
 }
Run Code Online (Sandbox Code Playgroud)

优点 快速,可能很简单。

缺点 枚举的大小将随着收据类型的增加而不断增加,导致代码不可维护。

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


3.反射的使用

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

缺点 1. 速度较慢

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

  2. 从收据获取值的逻辑被移至 setter,而不是像 HashMap 那样的构造函数


4.策略模式模板模式的使用

优点易于理解且比反射更快

缺点可能有点矫枉过正。将为每种类型的收据添加一个新类别。


考虑到上述所有要点,对于我的用例来说,哪种方法是删除多个 if-else 块的最佳方法?

Pet*_*rey 3

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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