1. 程式人生 > >利用策略列舉對討厭的Switch Case 語句進行重構

利用策略列舉對討厭的Switch Case 語句進行重構

前言:

重構發生的時機應該在什麼時候呢?

正確:一邊寫著程式碼,當完成之後,馬上想著是否能夠進行重構。

錯誤:寫完程式碼之後,等以後有時間去重構。

即重構本來就不是一件應該特別撥出時間做的事情,重構應該隨時隨地進行。

書上有這麼一句話,我非常喜歡:懶惰是程式設計師的一種美德。正如有句話說,不要讓你的“勤奮”毀掉了你,有著異曲同工之妙。《重構改善既有程式碼的設計》的作者說,“我是個很懶惰的程式設計師,我的懶惰表現形式之一就是:我總是記不住自己寫過的程式碼”,“我不是個偉大的程式設計師,我只是有著一些優秀習慣的好程式設計師”。

什麼是重構:

在不改變程式碼外部行為的前提下,對程式碼做出修改,以改程序序的內部結構。本質上說,重構是在程式碼寫好之後改進它的設計(注意:

重構不一定能夠提高效能,它提供了一種更高效且受控的程式碼整理技術,重構又與重寫不同,重寫程式碼是發生在現有的程式碼根本不能工作)。

舉個栗子:(在筆試的時候經常會有類似重構的例子)

場景:一個影片出租店的程式,計算每一位顧客的消費金額並列印詳單。操作者告訴程式:顧客租了哪些影片、租期多長,程式便根據租賃時間和影片單算出費用。影片分為三類:普通片、兒童片和新片。除了計算費用,還要為常客計算積分,積分會根據租片種類是否為新片而有不同。

未重構前的程式碼如下:

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 語句進行重構

前言: 重構發生的時機應該在什麼時候呢? 正確:一邊寫著程式碼,當完成之後,馬上想著是否能夠進行重構。 錯誤:寫完程式碼之後,等以後有時間去重構。 即重構本來就不是一件應該特別撥出時間做的事情,重構應該隨時隨地進行。 書上有這麼一句話,我非常喜歡:懶惰是程式設計師的一種美德

java switch case 語句列舉類 實現判斷

首先定義列舉類,如: public enum DataTypeEnum {     /**小時型別值**/     HOUR("hour"),     /**小時型別值**/     DAY("day"),     /**小時型別值**/     WEEK("week")

C語言中switch...case語句中break的重要性

不能 實現 比例 重要性 case語句 毫無 ... 應該 switch 在C語言中switch...case語句是經常用到的,下面我介紹一下在使用該語句時候需要註意的一個細節問題。話不多說,直接舉例子: 例子1: switch(fruit) { case 1:printf

switchcase 語句的用法

[] other sta rgs bsp str 復制代碼 ring 表達 public class Test7 { public static void main(String[] args) { int i=5; switch

Switch Case語句中多個值匹配同一個代碼塊的寫法

har com arch mssql pre html www ase cas switch ($p) { case ‘home‘: case ‘‘: $current_home = ‘current‘; break

JavaScript基礎知識(if、if else、else if、while、switch...case語句

case語句 bubuko ... gpo 控制 java 包含 分享 if...else 13、語句 概念:就是分號(;) 代表一條語句的結束 習慣:一行只編寫一條語句;一行編寫多條語句(代碼可讀性較差) 語句塊:可以包含多條語句 "{ }"將多條語句包裹 u 條

Java中的switch-case語句

sub public return ID PE stat class a case cti class ArithmeticFunction {   public static int arithmetic(int a, int b, String operator) {

switch case語句中能否作用在String,long上

bsp lips case語句 nbsp string 類型 span 出了 byte 在之前的eclipse中使用switch的case語句時是只能為(byte,short,char)int類型或枚舉類型。但在jdk1.7以後 在case語句中是可以使用String 以

Python 類似switch/case語句實現方法 獲取文件內容匹配函數並執行

lin get err 容易 main ref 設計 case error 這個主要提供了一種思路,這個不太好理解,我徹底敲了一遍,心裏有點低。參考下面的文章標題:Python switch/case語句實現方法來源:https://blog.csdn.net/l46013

ST語言和C語言關於case of 和switch case語句的區別

C語言中,case後不可直接跟多個常量,要如下圖所示使用(不要忘記defalut) switch(int,char){ case 1: case 3: case 5: case 7: case 8: case 10: case 12: //todo break; defalut: br

if語句,if...else if語句switch...case語句的區別和分析

當我們有一個判斷條件的時候,顯然用if語句比較方便有效。但當判斷條件很多的時候,我們可以使用if語句或者if....eles 語句和switch  case 語句。 if...else if語句和多個if語句的區別還是很大的,if...else if在任何一個環節滿足條件的時候就將會終

用 Python 實現簡單的 switch/case 語句

在Python中是沒有Switch / Case語句的,很多人認為這種語句不夠優雅靈活,在Python中用字典來處理多條件匹配問題字典會更簡單高效,對於有一定經驗的Python玩家不得不承認,的確如此。 但今天我們還是來看看如果一定要用Python來Switch /

switch...case語句的理解案例

switch語句 語法:switch(變數) {                 case 常量值1:        &n

java switch..case語句

語法: switch(變數){ case 變數值1: 程式碼塊1; break; case 變數值2: 程式碼塊2; break; ... case default: 程式碼塊d; break; } swit

C++的if/else語句switch/case語句

有時候,程式碼中需要實現這樣一個功能:當一個條件為真時做一件事,為假時做另一件事。這就引出了if/else語句。 if/else語句規則如下: 1. 標準格式: if(條件){ //條件為真時做 } else{ //條件為假時做 } 2. 如果條件為假時要什麼都不做,

Java: switch......case.....語句

switch(expression){ case value : //語句 break; //可選 case value : //語句 break; //可選 //你可以有任意數量的case語句 default

用陣列代替if-else和switch-case語句

       表驅動法(Table-Driven Approach),通過在表中查詢資訊,來代替很多複雜的if-else或者switch-case邏輯判斷。這是一種設計的技巧,可以應用很多的場合,不僅可以提高程式的效能,也能大大減少程式碼量,使得程式碼變得高效和優雅。下面將

簡單的switch case語句

簡單的switch case語句示例 public static void main(String[] args) { // TODO Auto-generated method stub Scanner in = new Scanner(System.in); while

switch-case語句裡面有return了 ,break還起作用嗎?該如何解決

switch-case語句裡面有return了 ,break還起作用嗎?switch-case語句裡面有return了   ,break還起作用嗎?     比如:                       switch(ID)                      

switch case 語句要注意!!!

c語言中的switch case 語句相比大家也是非常的清楚的。 無論是誰,在編寫程式碼的時候都會用到這個語句的 ``````````````````````````````````````````