[Day 01] 單例模式及簡單工廠設計模式


設計模式

設計模式是用來解決重複性問題,但是不同的語言有不同的特性,所以就算是相同的設計模式在不同的語言,會有所差異。

為何要用設計模式?

什麼!又要改,是要改幾次啊!!!?

時常聽到軟體工程師講這句話,不論事前如何謹慎規劃系統架構,我們仍會遇到需求變更、bug等,使軟體工程師不斷修改程式碼,不論是敏捷開發的SOLID原則或是設計模式,共同目標是為了讓程式碼更容易維護且保有擴充性。

物件導向程式設計SOLID原則

S: 單一職責(SRP)

單一職責指一個類別只負責一件事,但要注意的是並非一個類別只有一個方法,就如同一台車要在路上行駛,並不代別這台車只擁有路上行駛這個方法,事實上是由前進、後退、左轉、右轉、煞車等功能組成。從另一個角度來看,雖一台車由很多機構組成,每個機構的功能也有所不同,但駕駛人只需要了解如何開車,不用清楚內部結構,對於技師來說,才需要了解零件功能,所以規劃一個類別的責任需視需求而定。

O: 開放/封閉原則(OCP)

開放/封閉原則指的是一個物件導向程式設計時,必須保有開放(擴充性),隨時可以擴充新功能,但在擴充需求時又不影響到舊功能,故舊程式碼應為封閉修改的。

L: 替換原則(LSP)

子類別可以替換掉父類別而不影響程式架構,也就是說子類別可以執行父類別想做的事。

I: 介面隔離原則(ISP)

將不同的功能透過介面分離出來。

D: 依賴反轉原則(DIP)

高階模組不該依賴低階模組,兩個都應依賴於抽象概念上;抽象不依賴細節,而是細節依賴在抽象概念。

單例模式(Singleton)

  • 保證一個類別只能產生一個物件
  • 須提供存取該物件的統一方法
  • 貪婪單例模式:一開始new類別的instance,並將建構子宣告為private,這樣其他程式就無法再new出新物件
#define NULL nullptr  
#include <iostream>
using namespace std;

class Singleton {
private:
    volatile static Singleton *instance;
    Singleton() {
        cout << "建立物件..." << endl;
    }
public:
    volatile static Singleton *getInstance() {
        if (instance == NULL) {
            instance = new Singleton();
        }
        return instance;
    }
};
volatile Singleton* Singleton::instance = NULL;

int main() {
    volatile Singleton *instance1 = Singleton::getInstance();
    volatile Singleton *instance2 = Singleton::getInstance();
    int addr1 = (int)instance1;
    int addr2 = (int)instance2;
    cout << "位址1 = " << hex << addr1 << endl;
    cout << "位址2 = " << hex << addr2 << endl;
    delete instance1;
    return 0;
}

簡單工廠模式(Simple Factory)

  • 使用者透過工廠來取得產品
  • 使用者無需知道工廠如何生產產品
    簡單工廠模式又稱靜態工廠模式,傳入不同參數取得不同的類別物件。
    一訓練營為簡單的工廠,裡面的冒險者有弓箭手、鬥士等,冒險者則為產品的父類別,弓箭手與鬥士則為實體產品,如果有人要招募冒險者,僅需請訓練營幫忙訓練冒險者,無需理解訓練過程,如果要新增新型態冒險者,僅需於訓練冒險者的方法增加if else 或 switch case分支即可,但這樣直接修改訓練營的類別違反了開放/封閉原則,所以不算健全的設計模式,但用於小型的軟體架構中很好用,實務上也常會用到此種簡單的模式,而明天會進一步討論工廠模式及抽象工廠模式。

類別圖


#include <iostream>
#include <string>
using namespace std;

//產品(麵包)
class Bread
{
protected:
    string product_name;
public:
    void order() { 
        cout << "點餐: " << product_name << endl; 
    }
    void bake() { 
        cout << product_name << "烘烤中..." << endl; 
    }
    void finish() { 
        cout << product_name << "出爐!" << endl << endl; 
    }
};

//實作類別
class Bagel : public Bread
{
public:
    Bagel() {
        product_name = "貝果"; 
    }
};

class FrenchBread : public Bread
{
public:
    FrenchBread() {
        product_name = "法國麵包"; 
    }
};

class Toast : public Bread
{
public:
    Toast() {
        product_name = "吐司"; 
    }
};

//麵包工廠
Bread *BreadFactory(string name)
{
    Bread *bread = NULL;
    if (name == "Bagel") {
        bread = new Bagel();
    }
    else if (name == "French Bread") {
        bread = new FrenchBread();
    }
    else if (name == "Toast") {
        bread = new Toast();
    }
    else {
        bread = NULL;
    }
    while (bread != NULL)
    {
        bread -> order();
        bread -> bake();
        bread -> finish();
        return bread;
    }   
}

//測試碼
int main()
{
    Bread *order1 = BreadFactory("Bagel");
    Bread *order2 = BreadFactory("Toast");
    Bread *order3 = BreadFactory("French Bread");

    delete order1;
    delete order2;
    delete order3;

    return 0;
}

聲明: 內文為書摘,書本提供之範例用Java寫的,有興趣可以參考原著,以上C++程式為個人之練習,如有疏漏,歡迎前輩批評指教,謝謝!

#寫作松 #設計模式 #物件導向 #C++







你可能感興趣的文章

CH8-1 for迴圈

CH8-1 for迴圈

[ 紀錄 ] 實戰練習 - Todo List ( 以 JS 實作前端 + PHP 後端 )

[ 紀錄 ] 實戰練習 - Todo List ( 以 JS 實作前端 + PHP 後端 )

如何不使用 create-react-app 自己打造應用程式

如何不使用 create-react-app 自己打造應用程式






留言討論