1. 程式人生 > >《大話設計模式》之簡單工廠模式(Java版)

《大話設計模式》之簡單工廠模式(Java版)

##導火線: “請用C++、Java、C#或VB.NET任意一種面嚮物件語言實現一個計算器控制檯程式,要求輸入兩個數和運算子號,得到結果。”

#菜鳥階段的程式設計:

public class demo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("請輸入數字A:");
        String A = scanner.nextLine();
        System.out.println
("請選擇運算子號(+、-、*、/):"); String B = scanner.nextLine(); System.out.println("請輸入數字B:"); String C = scanner.nextLine(); String D = ""; if ("+".equals(B)){ D = (Double.parseDouble(A) + Double.parseDouble(C)) + ""; } if ("-".equals(B)){ D = (Double.parseDouble
(A) - Double.parseDouble(C)) + ""; } if ("*".equals(B)){ D = (Double.parseDouble(A) * Double.parseDouble(C)) + ""; } if ("/".equals(B)){ D = (Double.parseDouble(A) / Double.parseDouble(C)) + ""; } System.out.println("結果是:"+ D); }

上述程式碼存在的毛病:
程式碼毛病
上述程式碼程式碼規範及優化:

public class demo {
    public static void main(String[] args) {
        try {
            Scanner scanner = new Scanner(System.in);
            System.out.println("請輸入數字A:");
            String strNumberA = scanner.nextLine();
            System.out.println("請選擇運算子號(+、-、*、/):");
            String strOperator = scanner.nextLine();
            System.out.println("請輸入數字B:");
            String strNumberB = scanner.nextLine();
            String strResult = "";

            switch (strOperator)
            {
                case "+":
                    strResult = (Double.parseDouble(strNumberA) + Double.parseDouble(strNumberB)) + "";
                    break;
                case "-":
                    strResult = (Double.parseDouble(strNumberA) - Double.parseDouble(strNumberB)) + "";
                    break;
                case "*":
                    strResult = (Double.parseDouble(strNumberA) * Double.parseDouble(strNumberB)) + "";
                    break;
                case "/":
                    if (!"0".equals(strNumberB)){
                        strResult = (Double.parseDouble(strNumberA) / Double.parseDouble(strNumberB)) + "";
                    }
                    else{
                        strResult = "除數不能為0";
                    }
                    break;
            }
            System.out.println("結果是:"+ strResult);
        }catch (Exception ex){
            System.out.println("您的輸入有錯:" + ex.getMessage());
        }
    }
}

#面向物件的程式設計
“所有程式設計初學者都會有這樣的問題,就是碰到問題就直覺地用計算機能夠理解的邏輯來描述和表達待解決的問題及具體的求解過程。這其實是用計算機的方式去思考,比如計算器這個程式,先要求輸入兩個數和運算子號,然後根據運算子號判斷如何運算,得到結果,這本身沒有錯,但這樣的思維卻使得我們的程式只為滿足實現當前的需求,程式不容易維護,不容易擴充套件,更不容易複用。從而達不到高質量程式碼的要求。”
例子:【活體印刷】
這裡寫圖片描述
三國時期,由於還未發明活字印刷,所有要改字的時候,就必須要整個刻板全部重新刻!實在是很吐血很蛋痛!而到了後來,活體印刷的出現,此景此情此處只需更改四字即可,其餘工作都未白做,豈不妙哉!
“第一,要改,只需更改要改之字,此為可維護;第二,這些字並非用完這次就無用,完全可以在後來的印刷中重複使用,此乃可複用;第三,此詩若要加字,只需刻字加入即可,這是可擴充套件;第四,字的排列其實可能是豎排也可能是橫排,此時只需將活字移動就可做到滿足排列需求,此是靈活性好。”
“可維護、可複用、可擴充套件、靈活性好這四點,不僅僅是面向物件程式設計的一個起始點更是我們對待每一個程式應用一個較好的程式設計思想!(感悟)”

##導火線:基於以上的情景,要求你再寫一個Windows的計算器,你現在的程式碼能不能複用呢?

“有人說直接把程式碼複製過去不就可以了嗎?改動不多不算麻煩。這其實是非常不好的編碼習慣,因為當你的程式碼中重複的程式碼多到一定程度,維護的時候可能就是一場災難。越大的系統,這種方式帶來的問題越嚴重,程式設計有一原則,就是用盡可能的辦法去避免重複。回過頭看看我們寫的程式碼,有哪些是和控制檯(介面邏輯)無關的,而哪些又與計算器(業務邏輯)有關呢?”
“準確地說,就是讓業務邏輯與介面邏輯分開,讓它們之間的耦合度下降。只有分離開,才可以達到容易維護或擴充套件。”
#業務封裝的程式設計:

/**
 *  運算類
 * @author CHEN-JY
 *
 */
public class Operation {
    public static double GetResult(double numberA, double numberB, String operate)
    {
        double result = 0d;
        switch (operate)
        {
         case "+":
             result = numberA + numberB;
             break;
         case "-":
             result = numberA - numberB;
             break;
         case "*":
             result = numberA * numberB;
             break;
         case "/":         
                 result = numberA / numberB;                   
             break;
        }
        return result;
    }
}
/**
 * 業務與介面分離
 * @author CHEN-JY
 *
 */
public class ClassB {
    public static void main(String[] args) {        
        try {
             Scanner scanner = new Scanner(System.in);
             System.out.println("請輸入數字A:");
             String strNumberA = scanner.nextLine();
             System.out.println("請選擇運算子號(+、-、*、/):");
             String strOperator = scanner.nextLine();
             System.out.println("請輸入數字B:");
             String strNumberB = scanner.nextLine();
             String strResult = "";
             strResult = Operation.GetResult(Double.parseDouble(strNumberA), Double.parseDouble(strNumberB), strOperator) + "";
             System.out.println("結果是:"+ strResult);
        } catch (Exception e) {
            System.out.println("您的輸入有錯:" + e.getMessage());
        }
    }
}

如此的程式碼程式設計,不單是Windows程式,Web版程式需要運算也可以用它,PDA、手機等需要移動系統的軟體需要運算也可以用它。但僅此而已,還談不上完全面向物件,此處僅僅只用了面向物件三大特性中的其中一個(封裝),還有兩個沒用(繼承和多型)呢!
#緊耦合 VS 鬆耦合
:“基於以上的程式碼,是否已做到很靈活的可修改和擴充套件呢?”
條件:“現在如果我希望增加一個開根(sqrt)運算,你會如何改?”
菜鳥回答:“那隻需要改Operation類就行了,在switch中加一個分支就行了。”
分析:“問題是你要加一個平方根運算,卻需要讓加減乘除的運算都得來參與編譯,如果你一不小心把加法運算改成了減法,這豈不是大大的糟糕。打個比方,如果現在公司要求你為公司的薪資管理系統做維護,原來只有技術人員(月薪),市場銷售人員(底薪+提成),經理(年薪+股份)三種運算演算法,現在要增加兼職工作人員(時薪)的演算法,但按照上述的程式寫法,公司就必須要把包含原三種演算法的運算類給你,讓你修改,你如果心中小算盤一打,‘TMD!公司給我的工資這麼低,這下有機會了’,於是你除了增加了兼職演算法以外,在技術人員(月薪)演算法中寫了一句。

if (技術人員是我)
{
    salary *= 1.1;
}

如此就意味著,你的月薪每月都會增加10%(小心被抓去坐牢),本來是讓你加一個功能,卻使得原有執行良好的功能程式碼產生了變化,這個風險太大了!”
+繼承思想的程式設計:

/**
 * 運算類
 * @author CHEN-JY
 *
 */
public abstract class Operation {

    private double _numberA = 0;
    private double _numberB = 0;

    public double get_numberA() {
        return _numberA;
    }
    public void set_numberA(double _numberA) {
        this._numberA = _numberA;
    }
    public double get_numberB() {
        return _numberB;
    }
    public void set_numberB(double _numberB) {
        this._numberB = _numberB;
    }

    public abstract double getResult();
}
/**
 * 加減乘除類 -- 加法類,繼承運算類
 * @author CHNE-JY
 *
 */
public class OperationAdd extends Operation {   
    @Override
    public double getResult() {
        double result = 0;
        return get_numberA() + get_numberB();
    }
}
/**
 * 加減乘除類 -- 減法類,繼承運算類
 * @author CHNE-JY
 *
 */
public class OperationSub extends Operation {
    @Override
    public double getResult() {
        double result = 0;
        return get_numberA() * get_numberB();
    }

}
/**
 * 加減乘除類 -- 乘法類,繼承運算類
 * @author CHNE-JY
 *
 */
public class OperationMul extends Operation {   
    @Override
    public double getResult() {
        double result = 0;
        return get_numberA() + get_numberB();
    }
}
/**
 * 加減乘除類 -- 除法類,繼承運算類
 * @author CHNE-JY
 *
 */
public class OperationDiv extends Operation {   
    @Override
    public double getResult() {
        double result = 0;
        return get_numberA() / get_numberB();
    }
}

此時程式設計到了這,這樣要修改任何一個演算法就不需要提供其他演算法的程式碼了。但問題來了,我如何讓計算器知道我是希望用哪一個演算法呢?”
#簡單工廠模式的編碼:

/**
 * 簡單運算工廠類
 * @author CHEN-JY
 *
 */
public class OperationFacotry {
    public static Operation createOperate(String operate){      
        Operation oper = null;
        switch (operate)
        {
            case "+":
                oper = new OperationAdd();
                break;
            case "-":
                oper = new OperationSub();
                break;
            case "*":
                oper = new OperationMul();
                break;
            case "/":
                oper = new OperationDiv();
                break;
        }
        return oper;
    }   
}
/**
 * 客戶端程式碼
 * @author CHEN-JY
 *
 */
public class ClassC {

    public static void main(String[] args) {
        Operation oper;
        oper = OperationFacotry.createOperate("-");
        oper.set_numberA(1);
        oper.set_numberB(2);
        double result = oper.getResult();
        System.out.println("結果是:" + result);
    }

}

如此一來,那麼我們需要增加各種複雜運算,比如平方根,立方根,自然對數,正弦餘弦等,只需增加相應的運運算元類就可以了。
這裡寫圖片描述
簡單工廠模式是屬於建立型模式,又叫做靜態工廠方法(Static Factory Method)模式。簡單工廠模式是由一個工廠物件決定創建出哪一種產品類的例項。簡單工廠模式是工廠模式家族中最簡單實用的模式,可以理解為是不同工廠模式的一個特殊實現。 – 百度百科解釋