大話設計模式筆記の 簡單工廠模式

callmeDevil發表於2019-05-26

前言

個人風格系列筆記,不會太詳細,不喜勿噴,適用於複習設計模式而做。

概要

  • 一個好的程式猿/媛敲出來的程式碼應該是可維護、可複用、可擴充套件的,也就是具有較好的靈活性。
  • 為了達到以上目的,在還沒敲程式碼之前,需要事先考慮通過何種方式能夠使自己的程式的耦合度降低,最基本的便是物件導向的封裝、繼承、多型
  • 但這往往是不夠的,需要根據實際情況選擇合適的設計模式使得程式變得更加靈活,容易修改,並且易於複用
  • 複用並不是複製的意思。在實際開發當中,有時候會遇到一些類似的功能,有一部分已經是在別的 Service 中實現了(整個方法的功能是不一樣的),但可能出於專案趕進度的時候,也可能做這個功能的員工水平不足,於是乎直接將此處程式碼一頓操作 ctrl + C / V 一切搞定。但對日後維護的人來說是一場災難,萬一又是自己,日後少不了“早知如此,何必當初”的感嘆了。養成好的編碼習慣很重要。
  • 話說得好聽誰都會,正是因為人都有成長的過程,所以對業務的理解轉化為程式,以及編寫程式碼的優美程度也是一步一個腳印走出來的。是否能夠做到真正的鬆耦合,又或者“鬆”的過度導致程式過於臃腫,都跟個人的層次有關。付出不一定有收穫,但不付出,肯定沒有收穫。

拋磚引玉

案例

實現一個簡單計算器程式碼(加減乘除)

普通實現方式

/**
 * 運算類
 * Created by callmeDevil on 2019/5/26.
 */
public class Operation {

    /**
     * 獲取運算結果
     * @param numA 數值A
     * @param numB 數值B
     * @param operate 運算子
     * @return
     */
    public static double getResult(double numA, double numB, String operate) {
        double result = 0L;
        switch (operate) {
            case "+":
                result = numA + numB;
                break;
            case "-":
                result = numA - numB;
                break;
            case "*":
                result = numA * numB;
                break;
            case "/":
                result = numA / numB;
                break;
            default:
                break;
        }
        return result;
    }

}
/**
 * 測試類
 * Created by callmeDevil on 2019/5/26.
 */
public class Test {

    public static void main(String[] args) {
        Operation operation = new Operation();
        double numA = 10;
        double numB = 2;
        System.out.println("加法:" + operation.getResult(numA, numB, "+"));
        System.out.println("減法:" + operation.getResult(numA, numB, "-"));
        System.out.println("乘法:" + operation.getResult(numA, numB, "*"));
        System.out.println("除法:" + operation.getResult(numA, numB, "/"));
    }

}

但是這樣就有個問題,如果這個計算器還需要實現開根號運算呢?難道就直接在Operation類新增一個switch分支就行了嗎?不是的,這樣會讓原來良好執行的程式碼產生變化,風險太大,因此需要將各種運算的邏輯分離開來,此處引入簡單工廠模式

簡單工廠模式實現

定義

又稱為靜態工廠模式,根據工廠類傳入的引數動態決定建立某種類(這些類都繼承自同一父類或實現同一介面)的例項。

UML圖

大話設計模式筆記の 簡單工廠模式

結合程式碼有註釋

/**
 * 運算基礎類
 * Created by callmeDevil on 2019/5/26.
 */
public class BaseOperation {

    private double numA;
    private double numB;

    // 此處省略get、set方法

    /**
     * 獲取結果
     * @return
     */
    public double getResult() {
        double result = 0L;
        return result;
    }

}
/**
 * 加法運算
 * Created by callmeDevil on 2019/5/26.
 */
public class OperationAdd extends BaseOperation {

    @Override
    public double getResult() {
        return getNumA() + getNumB();
    }

}
/**
 * 減法運算
 * Created by callmeDevil on 2019/5/26.
 */
public class OperationSub extends BaseOperation {

    @Override
    public double getResult() {
        return getNumA() - getNumB();
    }

}
/**
 * 乘法運算
 * Created by callmeDevil on 2019/5/26.
 */
public class OperationMul extends BaseOperation {

    @Override
    public double getResult() {
        return getNumA() * getNumB();
    }

}
/**
 * 除法運算
 * Created by callmeDevil on 2019/5/26.
 */
public class OperationDiv extends BaseOperation {

    @Override
    public double getResult() {
        if (getNumB() == 0){
            throw new RuntimeException("除數不能為0!");
        }
        return getNumA() / getNumB();
    }

}
/**
 * 測試類
 * Created by callmeDevil on 2019/5/26.
 */
public class Test {

    public static void main(String[] args) {
        // 實現其他運算只需要更改輸入的運算子即可
        BaseOperation operation = OperationFactory.createOperation("+");
        operation.setNumA(10);
        operation.setNumB(2);
        System.out.println("加法:" + operation.getResult());
    }

}

相比普通實現方式的好處

將每種運算的具體實現分離在不同的類中,工廠類只需要負責根據輸入的運算子來建立對應的運算類例項即可。比如要一個開方運算是吧,那麼需要建立一個開方運算類,同樣繼承BaseOperation,接著在工廠類中switch中新增一個case分支,然後客戶端便能夠跟其他運算一樣呼叫。
可能有人會問,這裡不也同樣是修改了switch分支條件嗎?有什麼區別?問得好,說明有思考過,但是原因也很明顯,要仔細想想。把四種基礎運算具體實現分離出去,工廠類中就包含了四行建立例項的程式碼,這時修改了switch分支的風險,和普通實現方式的風險相比,哪個大已經不用細說了吧。

總結

這裡案例程式碼比較簡單,所以看起來會非常明朗,想必不必多說讀者應該也能稍有體會。假如每個型別的運算非常複雜,甚至與其他模組關聯,那使用設計模式的好處就體現出來了,比起全部程式碼堆在一個switch case上,在單獨的一個類裡面會更好維護和擴充套件吧?想做到完全不修改switch的程式碼是不可能,最重要的是我們達到了鬆耦合、可複用、可擴充套件目的,同時還降低了風險,有何不可。

相關文章