• 正文
    • 1 面向?qū)ο缶幊?/span>
    • 2 計(jì)算器實(shí)例
    • 3 總結(jié)
  • 推薦器件
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

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

2024/06/24
741
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

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

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

設(shè)計(jì)模式依賴與面向?qū)ο缶幊堂懿豢煞?,因此在開始學(xué)習(xí)設(shè)計(jì)模式之前,先簡單介紹下面向?qū)ο缶幊獭?/p>

先來看一個(gè)小故事:

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

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

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

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

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

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

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

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

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

那,問題出在哪里呢?

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

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

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

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

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

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

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

2 計(jì)算器實(shí)例

下面以一個(gè)計(jì)算器的代碼實(shí)例,來體會封裝的思想,以及簡單工廠模式的使用。

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

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

2.1 版本一:面向過程

第一個(gè)版本采用面向過程的思想,從接收用戶輸入,到數(shù)據(jù)運(yùn)算,以及最后的輸出,都是按順序在一個(gè)代碼塊中實(shí)現(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;
}

該程序的運(yùn)行效果如下圖所示:

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

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

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

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

計(jì)算部分的業(yè)務(wù)代碼,設(shè)計(jì)一個(gè)Operation運(yùn)算類,通過其成員函數(shù)GetResult來實(shí)現(xiàn)加減乘除運(yù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;
}

版本二的運(yùn)行效果演示如下:

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

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

比如,如果希望增加一個(gè)開根號的運(yùn)算,如果改?

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

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

2.3 版本三:簡單工廠

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

2.3.1 Operation運(yùn)算類(父類)

Operation運(yùn)算類為一個(gè)抽象類,是加減乘除類的父類。

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

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

2.3.2 加減乘除類(子類)

加減乘除子類通過公有繼承Operation類,可以訪問其共有成員變量numA和numB,并對GetResult方法進(jìn)行具體的實(shí)現(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 簡單運(yùn)算工廠類

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

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

// 簡單工廠模式
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ù)需要修改加法運(yùn)算,只需要修改OperationAdd類中的內(nèi)容即可,不會影響到其它計(jì)算類。

2.3.4 控制臺界面代碼

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

通過多態(tài),返回父類的方式,實(shí)現(xiàn)對應(yīng)運(yùn)算的計(jì)算結(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;
}

版本三的運(yùn)行效果演示如下:

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

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

    公有

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

    繼承

    空心三角+實(shí)線

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

    依賴

    箭頭+虛線

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

3 總結(jié)

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

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險(xiǎn)等級 參考價(jià)格 更多信息
ECS-.327-12.5-34QS-TR 1 ECS International Inc Parallel - Fundamental Quartz Crystal, 0.032768MHz Nom, SMD, 2 PIN
$1.16 查看
CPC1117N 1 IXYS Integrated Circuits Division Solid State Relay, TRANSISTOR OUTPUT SOLID STATE RELAY, 1500 V ISOLATION-MAX, ROHS COMPLIANT, MINIATURE, SOP-4
$1.95 查看
KSZ8041TLI-S 1 Microchip Technology Inc DATACOM, ETHERNET TRANSCEIVER, PQFP48
$3.03 查看

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計(jì)資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄