摘自<<clean-code>> 一书
写出短小的switch语句很难,当然也包括if/else在内。即便是只有两种条件的switch语句也要比单个的代码块或函数大的多。写出只做一件事的switch语句也很难。switch天生要做N件事。
不幸我们总无法避开switch语句,不过还是能够确保每个switch都埋藏在较低的抽象层级,而且永远不重复。当然,我们利用多态来实现。
public Money calculatePay(Employee e) throws InvalidEmployeeType {
switch(e.type){
case COMMISSIONED:
return calculateCommissionedPay(e);
case HOURLY:
return calculateHourlyPay(e);
case SALARIED:
return calculateSalariedPay(e);
default:
throw new InvalidEmployeeType(e.type);
}
}
以上代码有如下几个问题:
- 首先,它太长了,当出现新的雇用类型时,还会变得更长。
- 其次,它明显做了不止一件事。
- 第三,它违反了单一职责原则,因为有好几个修改它的理由。
- 第四,它违反了开放闭合原则,因为每当添加新的类型时,就必须修改之。
不过,该函数最麻烦的可能是到处皆有类似结构的函数。例如,可能会有:
isPayDay(Employee e, Date date)
或
deliverPay(Employee e, Money pay)
之类的,它们的结构都有相同的问题。
该问题的解决方案是将switch语句埋于抽象工厂底下,不让任何人看到。该工厂使用switch语句为Employee的派生物创建适当的实体,而不同的函数,如calculatePay 、isPayDay、deliverPay 等,则藉由Employee接口多态地接收派遣;
对于switch语句,如果只出现一次,用于创建多态对象,而且隐藏在某个继承关系中,在系统其他部分看不到。就还能容忍,当然也要就事论事。
public abstract class Employee{
public abstract boolean isPayDay();
public abstract Money calculatePay();
public abstract void deliverPay(Money pay);
}
public interface EmployeeFactory{
Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}
public class EmployeeFactoryImpl implements EmployeeFactory{{
public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType{
switch(r.type){
case COMMISSIONED:
return new CommissionedEmployee(r);
case HOURLY:
return new HourlyEmployee(r);
case SALARIED:
return new SalariedEmployee(r);
default:
throw new InvalidEmployeeType(r.type);
}
}
}