利用策略枚举对讨厌的Switch Case 语句进行重构

来源:互联网 发布:淘宝货源一件代发网 编辑:程序博客网 时间:2024/06/07 03:33

前言:

重构发生的时机应该在什么时候呢?

正确:一边写着代码,当完成之后,马上想着是否能够进行重构。

错误:写完代码之后,等以后有时间去重构。

即重构本来就不是一件应该特别拨出时间做的事情,重构应该随时随地进行。

书上有这么一句话,我非常喜欢:懒惰是程序员的一种美德。正如有句话说,不要让你的“勤奋”毁掉了你,有着异曲同工之妙。《重构改善既有代码的设计》的作者说,“我是个很懒惰的程序员,我的懒惰表现形式之一就是:我总是记不住自己写过的代码”,“我不是个伟大的程序员,我只是有着一些优秀习惯的好程序员”。

什么是重构:

在不改变代码外部行为的前提下,对代码做出修改,以改进程序的内部结构。本质上说,重构是在代码写好之后改进它的设计(注意:重构不一定能够提高性能,它提供了一种更高效且受控的代码整理技术,重构又与重写不同,重写代码是发生在现有的代码根本不能工作)。

举个栗子:(在笔试的时候经常会有类似重构的例子)

场景:一个影片出租店的程序,计算每一位顾客的消费金额并打印详单。操作者告诉程序:顾客租了哪些影片、租期多长,程序便根据租赁时间和影片单算出费用。影片分为三类:普通片、儿童片和新片。除了计算费用,还要为常客计算积分,积分会根据租片种类是否为新片而有不同。

未重构前的代码如下:

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;    }    }


原创粉丝点击