加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 1 面向?qū)ο缶幊?/span>
    • 2 計算器實例
    • 3 總結(jié)
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

《大話設(shè)計模式》解讀01-簡單工廠模式

06/24 10:31
638
閱讀需 17 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

本系列的文章,來介紹編程中的設(shè)計模式,介紹的內(nèi)容主要為《大話設(shè)計模式》的讀書筆記,并改用C++語言來實現(xiàn)(書中使用的是.NET中的C#),本篇來學(xué)習(xí)第一章,介紹的設(shè)計模式是——簡單工廠模式。

1 面向?qū)ο缶幊?/h2>

設(shè)計模式依賴與面向?qū)ο缶幊堂懿豢煞郑虼嗽陂_始學(xué)習(xí)設(shè)計模式之前,先簡單介紹下面向?qū)ο缶幊獭?/p>

先來看一個小故事:

話說三國時期,曹操在赤壁帶領(lǐng)百萬大軍,眼看就要滅掉東吳,統(tǒng)一天下,非常高性,于是大宴文武。

在酒席間,不覺吟到:“喝酒唱歌,人生真爽,......”,眾文武齊呼:“丞相好詩!”,

于是一臣子速速命令印刷工匠進行刻版印刷,以便流傳天下。

印刷工匠刻好樣張,拿出來給曹操一看,曹操感覺不妥,

說道:“喝與唱,此話過俗,應(yīng)該改為對酒當(dāng)歌較好!”,

于是臣子就命令工匠重新來過,工匠眼看連夜刻版之功,徹底白費,心中叫苦不迭,只得照辦。

印刷工匠再次刻好樣張,拿出來給曹操過目,曹操細細一品,覺得還是不好,

說:“人生真爽太過直接,應(yīng)該改為問句才夠意境,因此應(yīng)改為對酒當(dāng)歌,人生幾何”,

當(dāng)臣子再次轉(zhuǎn)告工匠之時,工匠暈倒......

那,問題出在哪里呢?

大概是三國時期還沒有活字印刷術(shù)吧,所以要改字的時候,就必須整個刻板全部重新雕刻。

如果有了活字印刷術(shù),其實只需要更改四個字即可,其余工作都未白做。

我們聯(lián)想編程,從這個小故事中,來體會一下編程中的一些思想:

    可維護:要改字,只需更改需要變動的字即可可復(fù)用:這些字并不是只是這次有用,后續(xù)如果在其它印刷中需要用,可重復(fù)使用可擴展:如果詩中需要加字,只需另外單獨刻字即可靈活性:字的排列可以橫排,也可以豎排

面向?qū)ο缶幊?,通過封裝、繼承和多態(tài),把程序的耦合度降低

傳統(tǒng)印刷術(shù)的問題就在于把所有字都刻在同一個版面上的耦合度太高。

使用設(shè)計模式可以使程序更加靈活,容易修改,并易于復(fù)用。

2 計算器實例

下面以一個計算器的代碼實例,來體會封裝的思想,以及簡單工廠模式的使用。

題目:設(shè)計一個計算器控制臺程序,輸入為兩個數(shù)和運算符,輸出結(jié)果

功能比較簡單,先來看第一個版本的實現(xiàn)。

2.1 版本一:面向過程

第一個版本采用面向過程的思想,從接收用戶輸入,到數(shù)據(jù)運算,以及最后的輸出,都是按順序在一個代碼塊中實現(xiàn)的:

int main()
{
    float numA = 0;
    float numB = 0;
    float result = 0;
    char operate;
    bool bSuccess = true;
    
    printf("please input a num A:n");
    scanf("%f", &numA);
    printf("please input a operate(+ - * ):n");
    std::cin >> operate;
    printf("please input a num B:n");
    scanf("%f", &numB);
    
    switch(operate)
    {
        case '+':
        {
            result = numA + numB;
            break;
        }
        case '-':
        {
            result = numA - numB;
            break;
        }
        case '*':
        {
            result = numA * numB;
            break;
        }
        case '/':
        {
            if (numB == 0)
            {
                bSuccess = false;
                printf("divisor cannot be 0!n");
                break;
            }
            result = numA / numB;
            break;
        }
        default:
        {
            bSuccess = false;
            break;
        }
    }
    
    if (bSuccess)
    {
        printf("%f %c %f = %fn", numA, operate, numB, result);
    }
    else
    {
        printf("[%f %c %f] calc fail!n", numA, operate, numB);
    }
    
    return 0;
}

該程序的運行效果如下圖所示:

上述代碼實現(xiàn)本身沒有什么問題,但是,如果現(xiàn)在要再實現(xiàn)一個帶有UI界面的計算器,代碼能不能復(fù)用呢?很顯然不行,代碼都是在一起的。

因此,為了便于代碼復(fù)用,可以將計算部分的代碼和顯示部分的代碼分開,降低它們之間的耦合度。

2.2 版本二:對業(yè)務(wù)封裝

版本二則是對計算部分的業(yè)務(wù)代碼顯示部分的控制臺輸入輸出代碼分開。

計算部分的業(yè)務(wù)代碼,設(shè)計一個Operation運算類,通過其成員函數(shù)GetResult來實現(xiàn)加減乘除運算。

2.2.1 業(yè)務(wù)代碼

class Operation
{
public:
    bool GetResult(float numA, float numB, char operate, float &result)
    {
        bool bSuccess = true;
        
        switch(operate)
        {
            case '+':
            {
                result = numA + numB;
                break;
            }
            case '-':
            {
                result = numA - numB;
                break;
            }
            case '*':
            {
                result = numA * numB;
                break;
            }
            case '/':
            {
                if (numB == 0)
                {
                    bSuccess = false;
                    printf("divisor cannot be 0!n");
                    break;
                }
                result = numA / numB;
                break;
            }
            default:
            {
                bSuccess = false;
                break;
            }
        }
        
        return bSuccess;
    }
};

2.2.2 控制臺界面代碼

顯示部分的控制臺輸入輸出代碼,還在main函數(shù)中。

int main()
{
    float numA = 0;
    float numB = 0;
    float result = 0;
    char operate;
    
    printf("please input a num A:n");
    scanf("%f", &numA);
    printf("please input a operate(+ - * ):n");
    std::cin >> operate;
    printf("please input a num B:n");
    scanf("%f", &numB);
    
    Operation Op1;
    bool bSuccess = Op1.GetResult(numA, numB, operate, result);
    
    if (bSuccess)
    {
        printf("%f %c %f = %fn", numA, operate, numB, result);
    }
    else
    {
        printf("[%f %c %f] calc fail!n", numA, operate, numB);
    }
    
    return 0;
}

版本二的運行效果演示如下:

上述的版本二的代碼實現(xiàn),就用到了面向?qū)ο笕筇匦灾械?strong>封裝。

那,上述代碼,是否可以做到靈活擴展?

比如,如果希望增加一個開根號的運算,如果改?

按照現(xiàn)有邏輯,需要修改Operation運算類,在switch中增加一個分支。但這樣,會需要加減乘除的邏輯再次參與編譯,另外,如果在修改開根號的代碼時,不小心改動了加減乘除的邏輯,影響就大了。

因此,可以使用面向?qū)ο笾?strong>繼承和多態(tài)的思想,來實現(xiàn)各個運算類的分離。

2.3 版本三:簡單工廠

版本三用到了封裝、繼承、多態(tài),以及通過簡單工廠來實例化出合適的對象。

2.3.1 Operation運算類(父類)

Operation運算類為一個抽象類,是加減乘除類的父類。

該類包含numA和numB兩個成員變量,以及一個虛函數(shù)GetResult用于計算運算結(jié)果,各個子類中對其進行具體的實現(xiàn)。

// 操作類(父類)
class Operation
{
public:
    float numA = 0;
    float numB = 0;
    
public:
    virtual float GetResult()
    {
        return 0;
    };
};

2.3.2 加減乘除類(子類)

加減乘除子類通過公有繼承Operation類,可以訪問其共有成員變量numA和numB,并對GetResult方法進行具體的實現(xiàn):

// 加法類(子類)
class OperationAdd : public Operation
{
public:
    float GetResult()
    {
        return numA + numB;
    }
};

// 減法類(子類)
class OperationSub : public Operation
{
public:
    float GetResult()
    {
        return numA - numB;
    }
};

// 乘法類(子類)
class OperationMul : public Operation
{
public:
    float GetResult()
    {
        return numA * numB;
    }
};

// 除法類(子類)
class OperationDiv : public Operation
{
public:
    float GetResult()
    {
        if (numB == 0)
        {
            printf("divisor cannot be 0!n");
            return 0;
        }
        return numA / numB;
    }
};

2.3.3 簡單運算工廠類

為了能方便地實例化加減乘除類,考慮使用一個單獨的類來做這個創(chuàng)造實例的過程,這個就是工廠。

設(shè)計一個OperationFactory類來實現(xiàn),這樣,只要輸入運算的符號,就能實例化出合適的對象。

// 簡單工廠模式
class OperationFactory
{
public:
    Operation *createOperation(char operation)
    {
        Operation *oper = nullptr;
        switch(operation)
        {
            case '+':
            {
                oper = (Operation *)(new OperationAdd());
                break;
            }
            case '-':
            {
                oper = (Operation *)(new OperationSub());
                break;
            }
            case '*':
            {
                oper = (Operation *)(new OperationMul());
                break;
            }
            case '/':
            {
                oper = (Operation *)(new OperationDiv());
                break;
            }
            default:
            {
                break;
            }
        }
        
        return oper;
    }
};

使用版本三,如果后續(xù)需要修改加法運算,只需要修改OperationAdd類中的內(nèi)容即可,不會影響到其它計算類。

2.3.4 控制臺界面代碼

顯示部分的控制臺輸入輸出代碼,還在main函數(shù)中。

通過多態(tài),返回父類的方式,實現(xiàn)對應(yīng)運算的計算結(jié)果。

{
    float numA = 0;
    float numB = 0;
    float result = 0;
    char operate;
    
    printf("please input a num A:n");
    scanf("%f", &numA);
    printf("please input a operate(+ - * ):n");
    std::cin >> operate;
    printf("please input a num B:n");
    scanf("%f", &numB);
    
    OperationFactory opFac;
    Operation *oper = nullptr;
    oper = opFac.createOperation(operate);
    if (oper != nullptr)
    {
        oper->numA = numA;
        oper->numB = numB;
        result = oper->GetResult();
        printf("%f %c %f = %fn", numA, operate, numB, result);
        
        delete oper;
    }
    else
    {
        printf("[%f %c %f] calc fail!n", numA, operate, numB);
    }
    
    return 0;
}

版本三的運行效果演示如下:

版本三中,各個類之間的關(guān)系如下圖所示:

      • 運算類是一個抽象類(類名用斜體表示),具有兩個float類型的

    公有

      • 的(共有用**+號**)成員變量numA和numB以及一個GetResult公有方法四個計算類繼承(

    繼承

    空心三角+實線

      • 表示)運算類,并實現(xiàn)對應(yīng)的GetResult方法簡單工廠類依賴于(

    依賴

    箭頭+虛線

    表示)運算類,通過createOperation方法實現(xiàn)運算類的實例化

3 總結(jié)

本篇主要介紹設(shè)計模式中的簡單工廠模式,首先通過一個活字印刷的小故事來體會程序設(shè)計中的可維護、可復(fù)用、可擴展、靈活性的思想,并引入面向?qū)ο笤O(shè)計模式中的三大基本思想:封裝、繼承、多態(tài),然后通過一個計算器的代碼實現(xiàn)的例子,通過C++實現(xiàn)了三個版本的代碼,由淺到深地理解面向?qū)ο蟮脑O(shè)計思想以及簡單工廠模式的使用。

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險等級 參考價格 更多信息
JS28F128J3F75A 1 Micron Technology Inc Flash, 8MX16, 75ns, PDSO56, 14 X 20 MM, LEAD FREE, TSOP-56
$12.8 查看
HFBR-1414MZ 1 Foxconn Transmitter, 792nm Min, 865nm Max, 160Mbps, ST Connector, DIP, Panel Mount, Through Hole Mount, ROHS COMPLIANT PACKAGE
$26.38 查看
MC100EP210SFAG 1 Rochester Electronics LLC 100E SERIES, LOW SKEW CLOCK DRIVER, 5 TRUE OUTPUT(S), 0 INVERTED OUTPUT(S), PQFP32, LEAD FREE, LQFP-32
$19.03 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

控制科學(xué)與工程碩士,日常分享單片機、嵌入式、C/C++、Linux等學(xué)習(xí)經(jīng)驗干貨~