学习笔记:Java 分类枚举和策略枚举(枚举的嵌套)

一、分类枚举,分门别类罗列。


我们总有把东西分门别类罗列的需求,比如一个餐厅,菜单(meal)分为开胃食物(Appetizer)、主菜(MainCourse)、甜点(Dessert)、咖啡(Coffee)。为了让程序更直观,可以这样写:
public interface Food {

    enum Appetizer implements Food {
        SALAD, SOUP, SPRING_ROLLS;
    }
    enum MainCourse implements Food {
        LASAGNE, BURRITO, PAD_THAI,
        LENTILS, HUMMOUS, VINDALOO;
    }
    enum Dessert implements Food {
        TIRAMISU, GELATO, BLACK_FOREST_CAKE,
        FRUIT, CREME_CARAMEL;
    }
    enum Coffee implements Food {
        BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,
        LATTE, CAPPUCCINO, TEA, HERB_TEA;
    }
}

这样使用:

public static void main(String[] args) {
    Food food = Food.Appetizer.SALAD;//开胃菜-->沙拉 
    food =     Food.MainCourse.PAD_THAI; 
} 

这样便实现了将事物分门别类罗列的目的。

二、策略枚举、枚举的嵌套

引用《Effective Java》中有关文章,

考虑用一个枚举表示薪资包中的工作天数。这个枚举有一个方法,根据给定某工人的基本工资(按小时)以及当天的工作时间,来计算他当天的报酬。在五个工作日中,超过正常八小时的工作时间都会产生加班工资;在双休日中,所有工作都产生加班工资。利用switch 语句,很容易通过将多个 case 标签分别应用到两个代码片段中,来完成这一计算。为了简洁起见,这个示例中的代码使用了 double ,但是注意 double 并不是适合薪资应用程序的数据类型。

 // Enum that switches on its value to share code - questionable
enum PayrollDay {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,
    private static final int HOURS_PER_SHIFT = 8;

    double pay(double hoursWorked, double payRate) {
        double basePay = hoursWorked * payRate;
        double overtimePay; // Calculate overtime pay
        switch(this) {
            case SATURDAY: case SUNDAY
                overtimePay = hoursWorked * payRate / 2;
            default: // Weekdays
                overtimePay = hoursWorked <= HOURS_PER_SHIFT ?
                0 : (hoursWorked - HOURS_PER_SHIFT) * payRate / 2;
                break;
        }
        return basePay + overtimePay;
    }

}

不可否认,这段代码十分简洁,但是从维护的角度来看,它十分危险。假设枚举中需要新增加一个元素,比如春节七天,加付3倍工资(overtimePay = hoursWorked *payRate*3),但是我们却忘记了在switch语句中添加相应的case。程序依然可以编译,但 pay 方法会悄悄地将春节的工资计算成与正常工作日的相同。
怎么解决这个问题呢?可以在枚举中将overtimePay的计算方法剥离出来,定义成一个虚函数,然后强制每个常量去实现它,这样我每次增加枚举元素时编译器便会要求我实现工资的计算方法,但是这样很繁琐,降低程序可读性的同时增加了很多重复代码,进而增加了出错的概率。
再进一步思考,overtimePay的计算方法也是可以分类的,而且是有限的,即可以将工资的计算做成一个策略枚举(strategy enum),形成一个工资计算策略,然后将这个策略作为PayrollDay构造器参数传递进去。这样,PayrollDay工资的计算就移交给工资计算策略了。

// The strategy enum pattern
enum PayrollDay {
    MONDAY(PayType.WEEKDAY), TUESDAY(PayType.WEEKDAY),
    WEDNESDAY(PayType.WEEKDAY),THURSDAY(PayType.WEEKDAY),
    FRIDAY(PayType.WEEKDAY),
    SATURDAY(PayType.WEEKEND), SUNDAY(PayType.WEEKEND),
    CHINESE_NEW_YEAR(PayType.CHINESE_NEW_YEAR);

    private PayType payType;

    private PayrollDay(PayType payType){
        this.payType = payType;
    }

    private double pay(double hoursWorked, double payRate) {
        return payType.pay(hoursWorked, payRate);
    }
    // The strategy enum type
    private enum PayType {
        WEEKDAY {
            double overtimePay(double hours, double payRate) {
                return hours <= HOURS_PER_SHIFT ? 0 :
                (hours - HOURS_PER_SHIFT) * payRate / 2;
            }
        },
        WEEKEND {
            double overtimePay(double hours, double payRate) {
                return hours * payRate / 2;
            }
        },
        CHINESE_NEW_YEAR {//春节的加班工资计算策略。
            @Override
            double overtimePay(double hours, double payRate) {
                return hours * payRate * 3;
            }
        };
        private static final int HOURS_PER_SHIFT = 8;
        abstract double overtimePay(double hours, double payRate);
        double pay(double hoursWorked, double payRate) {
            double basePay = hoursWorked * payRate;
            return basePay + overtimePay(hoursWorked, payRate);
        }
    }
}

当然,也可以采用接口的形式

// The strategy enum pattern
enum PayrollDay {
    MONDAY(OvertimePay.PAY_TYPE.WEEKDAY),TUESDAY(OvertimePay.PAY_TYPE.WEEKDAY),
    WEDNESDAY(OvertimePay.PAY_TYPE.WEEKDAY),THURSDAY(OvertimePay.PAY_TYPE.WEEKDAY),
    FRIDAY(OvertimePay.PAY_TYPE.WEEKDAY),
    SATURDAY(OvertimePay.PAY_TYPE.WEEKEND),SUNDAY(OvertimePay.PAY_TYPE.WEEKEND),
    CHINESE_NEW_YEAR(OvertimePay.PAY_TYPE.CHINESE_NEW_YEAR);

    private OvertimePay overtimePay;
    private PayrollDay(OvertimePay overtimePay){
        this.overtimePay = overtimePay;
    }

    double pay(double hours,double payRate){
        double basePay = hours * payRate;
        return basePay + overtimePay.overtimePay(hours,payRate);
    }
    private interface OvertimePay{
        enum PAY_TYPE implements OvertimePay{
            WEEKDAY {
                @Override
                public double overtimePay(double hours, double payRate) {
                    return hours < HOURS_PER_SHIFT ? 0 : (hours - HOURS_PER_SHIFT)*payRate/2 ;
                }
            },
            WEEKEND {
                @Override
                public double overtimePay(double hours, double payRate) {
                    return hours * payRate / 2;
                }
            },
            CHINESE_NEW_YEAR {
                @Override
                public double overtimePay(double hours, double payRate) {
                    return hours * payRate * 3;
                }
            };
        }
        double overtimePay(double hours,double payRate);
        static final int HOURS_PER_SHIFT = 8;
    }

你可能感兴趣的:(java)