利用策略枚举对讨厌的Switch Case 语句进行重构
2017-07-14 00:39
627 查看
前言:
重构发生的时机应该在什么时候呢?
正确:一边写着代码,当完成之后,马上想着是否能够进行重构。
错误:写完代码之后,等以后有时间去重构。
即重构本来就不是一件应该特别拨出时间做的事情,重构应该随时随地进行。
书上有这么一句话,我非常喜欢:懒惰是程序员的一种美德。正如有句话说,不要让你的“勤奋”毁掉了你,有着异曲同工之妙。《重构改善既有代码的设计》的作者说,“我是个很懒惰的程序员,我的懒惰表现形式之一就是:我总是记不住自己写过的代码”,“我不是个伟大的程序员,我只是有着一些优秀习惯的好程序员”。
什么是重构:
在不改变代码外部行为的前提下,对代码做出修改,以改进程序的内部结构。本质上说,重构是在代码写好之后改进它的设计(注意:重构不一定能够提高性能,它提供了一种更高效且受控的代码整理技术,重构又与重写不同,重写代码是发生在现有的代码根本不能工作)。
举个栗子:(在笔试的时候经常会有类似重构的例子)
场景:一个影片出租店的程序,计算每一位顾客的消费金额并打印详单。操作者告诉程序:顾客租了哪些影片、租期多长,程序便根据租赁时间和影片单算出费用。影片分为三类:普通片、儿童片和新片。除了计算费用,还要为常客计算积分,积分会根据租片种类是否为新片而有不同。
未重构前的代码如下:
分析:
首先,我一看到switch代码,就要看到分支的情况,这里case只有三个分支类型:Movie.REGULAR,Movie.NEW_RELEASE,Movie.CHILDRENS,如果Movie中新增了一种类型此时case就不支持,因此,至少要添加一个default分支,并且throw 自定义找不到该值的异常,这样程序更严谨。对该例重构的原则如下:
(1)对于switch case 这种语句,我习惯用策略枚举进行重构(具体用法参见:Effective Java 中文版 第2版P128-P136),通过枚举中枚举类型来分类型,对于不同类型处理具体策略采用定义一个抽象方法。
(2)根据高内聚、低耦合的原则,如果某个业务只是跟某个类相关,则将这个业务移动到该类中进行处理。
重构后的代码如下:
重构发生的时机应该在什么时候呢?
正确:一边写着代码,当完成之后,马上想着是否能够进行重构。
错误:写完代码之后,等以后有时间去重构。
即重构本来就不是一件应该特别拨出时间做的事情,重构应该随时随地进行。
书上有这么一句话,我非常喜欢:懒惰是程序员的一种美德。正如有句话说,不要让你的“勤奋”毁掉了你,有着异曲同工之妙。《重构改善既有代码的设计》的作者说,“我是个很懒惰的程序员,我的懒惰表现形式之一就是:我总是记不住自己写过的代码”,“我不是个伟大的程序员,我只是有着一些优秀习惯的好程序员”。
什么是重构:
在不改变代码外部行为的前提下,对代码做出修改,以改进程序的内部结构。本质上说,重构是在代码写好之后改进它的设计(注意:重构不一定能够提高性能,它提供了一种更高效且受控的代码整理技术,重构又与重写不同,重写代码是发生在现有的代码根本不能工作)。
举个栗子:(在笔试的时候经常会有类似重构的例子)
场景:一个影片出租店的程序,计算每一位顾客的消费金额并打印详单。操作者告诉程序:顾客租了哪些影片、租期多长,程序便根据租赁时间和影片单算出费用。影片分为三类:普通片、儿童片和新片。除了计算费用,还要为常客计算积分,积分会根据租片种类是否为新片而有不同。
未重构前的代码如下:
public class Customer { //Movie只是一个简单的纯数据类 private static class Movie{ public static final int CHILDRENS = 2; public static final int REGULAR = 0; public static final int NEW_RELEASE = 1; private String _title; private int _priceCode; public Movie(String title, int priceCode){ _title = title; _priceCode = priceCode; } public int getPriceCode(){ return _priceCode; } public void setPriceCode(int arg){ _priceCode = arg; } public String getTitle(){ return _title; } } //Rental表示某个租客租了一部电影 private static class Rental{ private Movie _movie; private int _daysRented; public Rental(Movie movie, int daysRented){ _movie = movie; _daysRented = daysRented; } public int getDaysRented(){ return _daysRented; } public Movie getMovie(){ return _movie; } } private String _name; private Vector _rentals = new Vector(); public Customer(String name){ _name = name; } public void addRental(Rental arg){ _rentals.addElement(arg); } public String getName(){ return _name; } //以下代码是需要重构的主题部分 public String statement(){ double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = "Rental Record for " + getName() + "\n"; while(rentals.hasMoreElements()){ double thisAmount = 0; Rental each = (Rental) rentals.nextElement(); //determine amounts for each line switch(each.getMovie().getPriceCode()){ case Movie.REGULAR: thisAmount += 2; if(each.getDaysRented() > 2) thisAmount += (each.getDaysRented() - 2) * 1.5; break; case Movie.NEW_RELEASE: thisAmount += each.getDaysRented() * 3; break; case Movie.CHILDRENS: thisAmount += 1.5; if(each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) * 1.5; break; } //add frequent renter points frequentRenterPoints ++; //add bonus for a two day new release rental if((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) frequentRenterPoints ++; //show figures for this rental result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(thisAmount) + "\n"; totalAmount += thisAmount; } //add footer lines result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points"; return result; } }
分析:
首先,我一看到switch代码,就要看到分支的情况,这里case只有三个分支类型:Movie.REGULAR,Movie.NEW_RELEASE,Movie.CHILDRENS,如果Movie中新增了一种类型此时case就不支持,因此,至少要添加一个default分支,并且throw 自定义找不到该值的异常,这样程序更严谨。对该例重构的原则如下:
(1)对于switch case 这种语句,我习惯用策略枚举进行重构(具体用法参见:Effective Java 中文版 第2版P128-P136),通过枚举中枚举类型来分类型,对于不同类型处理具体策略采用定义一个抽象方法。
(2)根据高内聚、低耦合的原则,如果某个业务只是跟某个类相关,则将这个业务移动到该类中进行处理。
重构后的代码如下:
public class CustomerV2 { private static class RentalV2{ private MovieV2 _movie; private int _daysRented; public RentalV2(MovieV2 movie, int daysRented){ _movie = movie; _daysRented = daysRented; } public int getDaysRented(){ return _daysRented; } public MovieV2 getMovie(){ return _movie; } public int getRenterPointers(){ if(_movie.equals(MovieV2.NEW_RELEASE) && _daysRented > 1) return 2; return 1; } } //通过策略枚举 private static enum MovieV2{ CHILDRENS(2) { @Override public double getAmount(int daysRented) { double amount = 0; amount += 2; if(daysRented > 2){ amount += (daysRented -2) * 1.5; } return amount; } }, REGULAR(0) { @Override public double getAmount(int daysRented) { double amount = 0; amount += daysRented * 3; return amount; } }, NEW_RELEASE(1) { @Override public double getAmount(int daysRented) { double amount = 0; if(daysRented > 3) amount += (daysRented -3) * 1.5; return amount; } }; private String _title; private final int _priceCode; MovieV2(int priceCode){ this._priceCode = priceCode; } public abstract double getAmount(int daysRented); } private String _name; private Vector _rentals = new Vector(); public CustomerV2(String name){ _name = name; } public void addRental(RentalV2 arg){ _rentals.addElement(arg); } public String getName(){ return _name; } //计算amount和fequent分开 public String statement(){ double totalAmount = getTotalAmounts(); int frequentRenterPointers = getFrequentRenterPointers(); return getReturnData(totalAmount, frequentRenterPointers); } private double getTotalAmounts(){ Enumeration rentals = _rentals.elements(); double totalAmount = 0; while(rentals.hasMoreElements()){ RentalV2 rental = (RentalV2) rentals.nextElement(); MovieV2 movie = rental.getMovie(); double amount = movie.getAmount(rental.getDaysRented()); totalAmount += amount; } return totalAmount; } private int getFrequentRenterPointers(){ Enumeration rentals = _rentals.elements(); int frequentRenterPointers = 0; while(rentals.hasMoreElements()){ RentalV2 rental = (RentalV2) rentals.nextElement(); frequentRenterPointers += rental.getRenterPointers(); } return frequentRenterPointers; } private String getReturnData(double totalAmount, int frequentRenterPointers){ String result = "Rental Record for " + getName() + "\n"; result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; result += "You earned " + String.valueOf(frequentRenterPointers) + " frequent renter points"; return result; } }
相关文章推荐
- 个人认为不错的代码结构:利用switch case语句进行参数的取值校验
- 利用switch语句进行多选一判断的实例代码
- 解析如何利用switch语句进行字符统计
- 利用switch语句进行多选一判断的实例代码
- 利用switch语句进行多选一判断。
- ? 枚举 编程题#2:拨钟问题(Coursera 程序设计与算法 专项课程4;函数memcpy的用法,switch case break 语句!)
- 重构:switch语句改成策略模式还是状态模式
- 利用case when then语句进行Oracle行列转换
- switch case语句case后的枚举常量不带枚举类型
- 利用for语句 + switch语句进行中奖判断和循环
- beego利用casbin进行权限管理——第四节 策略更新
- 利用组策略进行的一次Windows主机安全整改
- JAVA流程控制 IF ELSE语句与Switch Case语句的使用
- switch 和 case 语句的参数
- 智力题总结——求1+2+…+n, 要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)。
- 在switch中的case语句中声明变量编译出错的解决方案
- 利用Eclipse进行重构(上 下)
- 在Android library中不能使用switch-case语句访问资源ID的原因分析及解决方案
- 计算1-n的和(不用for, while, goto, if, else, switch, case和三目运算符, 也不用乘除法)---利用pow函数
- 计算1-n的和(不用for, while, goto, if, else, switch, case和三目运算符, 也不用乘除法)---利用多态性