Boolan網(wǎng)——C++微專業(yè)(設(shè)計(jì)模式)第一周學(xué)習(xí)筆記

(1)設(shè)計(jì)模式簡(jiǎn)介
以下代碼能夠?qū)崿F(xiàn)對(duì)圖形的繪制:

class Point{
public:
    int x;
    int y;
};

class Line{
public:
    Point start;
    Point end;

    Line(const Point& start, const Point& end){
        this->start = start;
        this->end = end;
    }

};

class Rect{
public:
    Point leftUp;
    int width;
    int height;

    Rect(const Point& leftUp, int width, int height){
        this->leftUp = leftUp;
        this->width = width;
        this->height = height;
    }

};



class MainForm : public Form {
private:
    Point p1;
    Point p2;

    vector<Line> lineVector;
    vector<Rect> rectVector;

public:
    MainForm(){
        //...
    }
protected:

    virtual void OnMouseDown(const MouseEventArgs& e);
    virtual void OnMouseUp(const MouseEventArgs& e);
    virtual void OnPaint(const PaintEventArgs& e);
};


void MainForm::OnMouseDown(const MouseEventArgs& e){
    p1.x = e.X;
    p1.y = e.Y;

    //...
    Form::OnMouseDown(e);
}

void MainForm::OnMouseUp(const MouseEventArgs& e){
    p2.x = e.X;
    p2.y = e.Y;

    if (rdoLine.Checked){
        Line line(p1, p2);
        lineVector.push_back(line);
    }
    else if (rdoRect.Checked){
        int width = abs(p2.x - p1.x);
        int height = abs(p2.y - p1.y);
        Rect rect(p1, width, height);
        rectVector.push_back(rect);
    }

    //...
    this->Refresh();

    Form::OnMouseUp(e);
}

void MainForm::OnPaint(const PaintEventArgs& e){

    //針對(duì)直線
    for (int i = 0; i < lineVector.size(); i++){
        e.Graphics.DrawLine(Pens.Red,
            lineVector[i].start.x,
            lineVector[i].start.y,
            lineVector[i].end.x,
            lineVector[i].end.y);
    }

    //針對(duì)矩形
    for (int i = 0; i < rectVector.size(); i++){
        e.Graphics.DrawRectangle(Pens.Red,
            rectVector[i].leftUp,
            rectVector[i].width,
            rectVector[i].height);
    }

    //...
    Form::OnPaint(e);
}

上述代碼體現(xiàn)了一種分而治之的思想,在MainForm的類里面對(duì)顯示界面的每個(gè)形狀都進(jìn)行了維護(hù)和處理。
在上述代碼中只是對(duì)于矩形、直線和點(diǎn)三種形式的圖形進(jìn)行了繪制方式的設(shè)定,若此時(shí)想繪制一個(gè)圓,此時(shí)代碼不能提供相對(duì)應(yīng)的方法,這也就意味著代碼將會(huì)發(fā)生變化。
需增加一個(gè)圓的類class Circle,同時(shí)還需要進(jìn)行如下更改:

class MainForm : public Form {
private:
    Point p1;
    Point p2;

    vector<Line> lineVector;
    vector<Rect> rectVector;
    //改變
    vector<Circle> circleVector;

public:
    MainForm(){
        //...
    }
protected:

    virtual void OnMouseDown(const MouseEventArgs& e);
    virtual void OnMouseUp(const MouseEventArgs& e);
    virtual void OnPaint(const PaintEventArgs& e);
};


void MainForm::OnMouseDown(const MouseEventArgs& e){
    p1.x = e.X;
    p1.y = e.Y;

    //...
    Form::OnMouseDown(e);
}

void MainForm::OnMouseUp(const MouseEventArgs& e){
    p2.x = e.X;
    p2.y = e.Y;

    if (rdoLine.Checked){
        Line line(p1, p2);
        lineVector.push_back(line);
    }
    else if (rdoRect.Checked){
        int width = abs(p2.x - p1.x);
        int height = abs(p2.y - p1.y);
        Rect rect(p1, width, height);
        rectVector.push_back(rect);
    }
    //改變
    else if (...){
        //...
        circleVector.push_back(circle);
    }

    //...
    this->Refresh();

    Form::OnMouseUp(e);
}

void MainForm::OnPaint(const PaintEventArgs& e){

    //針對(duì)直線
    for (int i = 0; i < lineVector.size(); i++){
        e.Graphics.DrawLine(Pens.Red,
            lineVector[i].start.x,
            lineVector[i].start.y,
            lineVector[i].end.x,
            lineVector[i].end.y);
    }

    //針對(duì)矩形
    for (int i = 0; i < rectVector.size(); i++){
        e.Graphics.DrawRectangle(Pens.Red,
            rectVector[i].leftUp,
            rectVector[i].width,
            rectVector[i].height);
    }

    //改變
    //針對(duì)圓形
    for (int i = 0; i < circleVector.size(); i++){
        e.Graphics.DrawCircle(Pens.Red,
            circleVector[i]);
    }

    //...
    Form::OnPaint(e);
}

上述修改的代碼段比較分散,如果站在軟件工程的角度上來看,對(duì)于修改過的部分都需要在重新測(cè)試等一些列的操作,其實(shí)對(duì)于功能擴(kuò)充來說其實(shí)面對(duì)著后期大量的工作。

關(guān)于抽象的設(shè)計(jì)思路:

class Shape{
public:
    virtual void Draw(const Graphics& g)=0;
    virtual ~Shape() { }
};


class Point{
public:
    int x;
    int y;
};

class Line: public Shape{
public:
    Point start;
    Point end;

    Line(const Point& start, const Point& end){
        this->start = start;
        this->end = end;
    }

    //實(shí)現(xiàn)自己的Draw,負(fù)責(zé)畫自己
    virtual void Draw(const Graphics& g){
        g.DrawLine(Pens.Red,
            start.x, start.y,end.x, end.y);
    }

};

class Rect: public Shape{
public:
    Point leftUp;
    int width;
    int height;

    Rect(const Point& leftUp, int width, int height){
        this->leftUp = leftUp;
        this->width = width;
        this->height = height;
    }

    //實(shí)現(xiàn)自己的Draw,負(fù)責(zé)畫自己
    virtual void Draw(const Graphics& g){
        g.DrawRectangle(Pens.Red,
            leftUp,width,height);
    }

};


class MainForm : public Form {
private:
    Point p1;
    Point p2;

    //針對(duì)所有形狀
    vector<Shape*> shapeVector;

public:
    MainForm(){
        //...
    }
protected:

    virtual void OnMouseDown(const MouseEventArgs& e);
    virtual void OnMouseUp(const MouseEventArgs& e);
    virtual void OnPaint(const PaintEventArgs& e);
};


void MainForm::OnMouseDown(const MouseEventArgs& e){
    p1.x = e.X;
    p1.y = e.Y;

    //...
    Form::OnMouseDown(e);
}

void MainForm::OnMouseUp(const MouseEventArgs& e){
    p2.x = e.X;
    p2.y = e.Y;

    if (rdoLine.Checked){
        shapeVector.push_back(new Line(p1,p2));
    }
    else if (rdoRect.Checked){
        int width = abs(p2.x - p1.x);
        int height = abs(p2.y - p1.y);
        shapeVector.push_back(new Rect(p1, width, height));
    }
    else if (...){
        //...
        shapeVector.push_back(circle);
    }

    //...
    this->Refresh();

    Form::OnMouseUp(e);
}

void MainForm::OnPaint(const PaintEventArgs& e){

    //針對(duì)所有形狀
    for (int i = 0; i < shapeVector.size(); i++){

        shapeVector[i]->Draw(e.Graphics); //多態(tài)調(diào)用,各負(fù)其責(zé)
    }

    //...
    Form::OnPaint(e);
}

對(duì)于每個(gè)形狀,都增加了一個(gè)共同的父類Shape,同時(shí)對(duì)于圖形來說應(yīng)該如何繪制自己的這樣的形狀的函數(shù)都是由自己來實(shí)現(xiàn)的(Draw()函數(shù))。MainForm只需要管理Shape的指針,而不再具體管理某一個(gè)具體的形狀,實(shí)現(xiàn)了一次向上抽象的管理。同時(shí)onPaint的方法來說,也不在直接管理繪制的問題,而是調(diào)用了Shape中的虛函數(shù)Draw,通過多態(tài)調(diào)用來實(shí)現(xiàn)了MainForm來調(diào)用了自己繪制自己的方法。
此時(shí)若再次需要添加一個(gè)對(duì)圓形的處理,則其偽碼如下:

class Shape{
public:
    virtual void Draw(const Graphics& g)=0;
    virtual ~Shape() { }
};


class Point{
public:
    int x;
    int y;
};

class Line: public Shape{
public:
    Point start;
    Point end;

    Line(const Point& start, const Point& end){
        this->start = start;
        this->end = end;
    }

    //實(shí)現(xiàn)自己的Draw,負(fù)責(zé)畫自己
    virtual void Draw(const Graphics& g){
        g.DrawLine(Pens.Red,
            start.x, start.y,end.x, end.y);
    }

};

class Rect: public Shape{
public:
    Point leftUp;
    int width;
    int height;

    Rect(const Point& leftUp, int width, int height){
        this->leftUp = leftUp;
        this->width = width;
        this->height = height;
    }

    //實(shí)現(xiàn)自己的Draw,負(fù)責(zé)畫自己
    virtual void Draw(const Graphics& g){
        g.DrawRectangle(Pens.Red,
            leftUp,width,height);
    }

};

//增加
class Circle : public Shape{
public:
    //實(shí)現(xiàn)自己的Draw,負(fù)責(zé)畫自己
    virtual void Draw(const Graphics& g){
        g.DrawCircle(Pens.Red,
            ...);
    }

};

class MainForm : public Form {
private:
    Point p1;
    Point p2;

    //針對(duì)所有形狀
    vector<Shape*> shapeVector;

public:
    MainForm(){
        //...
    }
protected:

    virtual void OnMouseDown(const MouseEventArgs& e);
    virtual void OnMouseUp(const MouseEventArgs& e);
    virtual void OnPaint(const PaintEventArgs& e);
};


void MainForm::OnMouseDown(const MouseEventArgs& e){
    p1.x = e.X;
    p1.y = e.Y;

    //...
    Form::OnMouseDown(e);
}

void MainForm::OnMouseUp(const MouseEventArgs& e){
    p2.x = e.X;
    p2.y = e.Y;

    if (rdoLine.Checked){
        shapeVector.push_back(new Line(p1,p2));
    }
    else if (rdoRect.Checked){
        int width = abs(p2.x - p1.x);
        int height = abs(p2.y - p1.y);
        shapeVector.push_back(new Rect(p1, width, height));
    }
    //改變
    else if (...){
        //...
        shapeVector.push_back(circle);
    }

    //...
    this->Refresh();

    Form::OnMouseUp(e);
}

void MainForm::OnPaint(const PaintEventArgs& e){

    //針對(duì)所有形狀
    for (int i = 0; i < shapeVector.size(); i++){

        shapeVector[i]->Draw(e.Graphics); //多態(tài)調(diào)用,各負(fù)其責(zé)
    }

    //...
    Form::OnPaint(e);
}

以上可以看出解決了之前代碼所存在問題,由于這樣的抽象的思想,使得代碼的變更更加容易,重用性得到了提升。
什么是好的軟件設(shè)計(jì)呢?軟件設(shè)計(jì)的金科玉律:復(fù)用。
變化是復(fù)用最大的天敵!面向?qū)ο蟮淖畲髢?yōu)勢(shì)就在于:抵御變化。
(2)面向?qū)ο笤O(shè)計(jì)原則
<1>依賴倒置原則(DIP)
1)高層模塊(穩(wěn)定的)不應(yīng)該依賴于低層模塊(容易變化的),二者都應(yīng)該依賴于抽象(穩(wěn)定的);
2)抽象(穩(wěn)定的)不應(yīng)該依賴于實(shí)現(xiàn)細(xì)節(jié)(容易變化的),實(shí)現(xiàn)細(xì)節(jié)應(yīng)該依賴于抽象(穩(wěn)定的)。

<2>開放封閉原則(OCP)
1)對(duì)擴(kuò)展開放,對(duì)更改封閉;
2)模塊應(yīng)該是可擴(kuò)展的,但是不可修改。

<3>單一指責(zé)原則(SRP)
1)一個(gè)類應(yīng)該僅有一個(gè)引起它變化的原因;
2)變化的方向隱含著類的責(zé)任。

<4>Liskov替換原則(LSP)
1)子類必須能夠替換他們的基類(is-a);
2)集成表達(dá)抽象類型。

<5>接口隔離原則(ISP)
1)不應(yīng)該強(qiáng)迫客戶程序依賴他們不用的方法;
2)接口應(yīng)該小而完備。

<6>優(yōu)先使用對(duì)象組合,而不是類繼承
1)類繼承通常為“白盒復(fù)用”,對(duì)象組合通常為“黑箱復(fù)用”;
2)繼承在某種程度上破壞了封裝性,子類父類耦合度高;
3)而對(duì)象組合則只要求被組合的對(duì)象具有良好定義的接口,耦合度低。

<7>封裝變化點(diǎn)
使用封裝來創(chuàng)建對(duì)象之間的分界層,讓設(shè)計(jì)者可以在分界層的一側(cè)進(jìn)行修改,而不會(huì)對(duì)另一側(cè)產(chǎn)生不良的影響,從而實(shí)現(xiàn)層次間的松耦合。

<8>針對(duì)接口編程,而不是針對(duì)實(shí)現(xiàn)編程
1)不講變量類型聲明為某個(gè)特定的具體類,而是聲明為某個(gè)接口;
2)客戶程序無需獲知對(duì)象的具體類型,只需要知道對(duì)象所具有的接口;
3)減少系統(tǒng)中個(gè)部分的依賴關(guān)系,從而實(shí)現(xiàn)“高內(nèi)聚、松耦合”的類型設(shè)計(jì)方案。

產(chǎn)業(yè)強(qiáng)盛的標(biāo)志:接口的標(biāo)準(zhǔn)化

GOF-23模式分類方式:
<1>依據(jù)目的分類
1)創(chuàng)建型(Creational)模式
2)結(jié)構(gòu)型(Structural)模式
3)行為型(Behavioral)模式
<2>依據(jù)范圍分類
1)類模式處理與子類的靜態(tài)關(guān)系
2)對(duì)象模式處理間的動(dòng)態(tài)關(guān)系
(3)模板方法
定一個(gè)操作中的算法的骨架(穩(wěn)定的),而將一些步驟延遲(容易變化的)到子類中。Template Method使得子類可以不改變(復(fù)用)一個(gè)算法的結(jié)構(gòu)即可重新定義(Override覆寫)該算法的某寫特定步驟
——《設(shè)計(jì)模式(GoF)》
對(duì)于庫(kù)的開發(fā)人員來說,已經(jīng)開發(fā)完成了所需要功能的1、3、5三個(gè)步驟;而如果要實(shí)現(xiàn)這個(gè)完整功能,需要應(yīng)用程序的開發(fā)人員實(shí)現(xiàn)其中的2和4這兩步。同時(shí)還需要應(yīng)用程序的開發(fā)者在使用的時(shí)候依次來調(diào)用庫(kù)和自己開發(fā)的這幾個(gè)函數(shù)。

//程序庫(kù)開發(fā)人員
class Library{

public:
    void Step1(){
        //...
    }

    void Step3(){
        //...
    }

    void Step5(){
        //...
    }
};

//應(yīng)用程序開發(fā)人員
class Application{
public:
    bool Step2(){
        //...
    }

    void Step4(){
        //...
    }
};

int main()
{
    Library lib();
    Application app();

    lib.Step1();

    if (app.Step2()){
        lib.Step3();
    }

    for (int i = 0; i < 4; i++){
        app.Step4();
    }

    lib.Step5();

}

上述開發(fā)方式存在著以下問題:
<1>對(duì)應(yīng)用程序的開發(fā)人員來說,他需要自己開發(fā)其中的第二和第五步的函數(shù)的開發(fā)。對(duì)于應(yīng)用程序的開發(fā)者來說,要求是比較高的,必須對(duì)庫(kù)中的函數(shù)情況比較了解,重寫的兩個(gè)函數(shù)的難度也相對(duì)較大,對(duì)于函數(shù)整體執(zhí)行流程也不被庫(kù)函數(shù)的開發(fā)人員所控制。
<2>庫(kù)開發(fā)人員和應(yīng)用程序的開發(fā)人員所開發(fā)的內(nèi)容的耦合度很高,彼此相互交織在一起,還需要由用開發(fā)人員來組織整體調(diào)用流程。未來程序的擴(kuò)展性和可維護(hù)性的難度都比較大。
通過模板方法可以解決以上問題:

//程序庫(kù)開發(fā)人員
class Library{
public:
    //穩(wěn)定 template method
    void Run(){

        Step1();

        if (Step2()) { //支持變化 ==> 虛函數(shù)的多態(tài)調(diào)用
            Step3();
        }

        for (int i = 0; i < 4; i++){
            Step4(); //支持變化 ==> 虛函數(shù)的多態(tài)調(diào)用
        }

        Step5();

    }
    virtual ~Library(){ }

protected:

    void Step1() { //穩(wěn)定
        //.....
    }
    void Step3() {//穩(wěn)定
        //.....
    }
    void Step5() { //穩(wěn)定
        //.....
    }

    virtual bool Step2() = 0;//變化
    virtual void Step4() =0; //變化
};

//應(yīng)用程序開發(fā)人員
class Application : public Library {
protected:
    virtual bool Step2(){
        //... 子類重寫實(shí)現(xiàn)
    }

    virtual void Step4() {
        //... 子類重寫實(shí)現(xiàn)
    }
};

int main()
    {
        Library* pLib=new Application();
        lib->Run();

        delete pLib;
    }
}

能夠開發(fā)庫(kù)的開發(fā)人員能增加了兩個(gè)虛函數(shù),同時(shí)庫(kù)的開發(fā)人員定義了一個(gè)run方法,在run方法中,按照步驟調(diào)用了按照規(guī)則來調(diào)用的幾個(gè)方法。
此時(shí)應(yīng)用程序的開發(fā)人員只需要重寫庫(kù)函數(shù)中定義的函數(shù)。這樣不僅避免了可能出現(xiàn)的缺陷,同時(shí)對(duì)于應(yīng)用程序來說使用庫(kù)的流程變得極其簡(jiǎn)單,只需要調(diào)用run方法即可,而不需要考慮具體的調(diào)用流程和規(guī)則。
對(duì)于Template Method來說,有一個(gè)前提,就是Run()方法必須要是穩(wěn)定的。如果Run()不穩(wěn)定,那么沒有一個(gè)穩(wěn)定的軟件的骨架,就不存在這樣一種設(shè)計(jì)模式。假定,所有方式都是穩(wěn)定,那么其實(shí)也沒有必要使用設(shè)計(jì)模式。
Template Method是一種非?;A(chǔ)性的設(shè)計(jì)模式,在面向?qū)ο蟮南到y(tǒng)中有著大量的應(yīng)用。他用最簡(jiǎn)潔的機(jī)制(虛函數(shù)的多態(tài)性)為很多應(yīng)用程序的框架提供了靈活的擴(kuò)展點(diǎn),是代碼復(fù)用方面的基本實(shí)現(xiàn)結(jié)構(gòu)。
除了可以靈活對(duì)應(yīng)子步驟的變化外,“不要調(diào)用我,讓我來調(diào)用你”的反向控制結(jié)構(gòu)是Template Method的典型應(yīng)用。
在具體實(shí)現(xiàn)方面,被Template Method調(diào)用的虛方法可以具有實(shí)現(xiàn),也可以沒有任何實(shí)現(xiàn)(抽象方法、純虛方法),一般推薦將他們?cè)O(shè)置為protected方法。
(4)策略模式
定義一系列算法,把他們一個(gè)個(gè)封裝起來,并且是他們可以互相替換(變化)。該模式似的算法可以獨(dú)立于使用它的客戶程序(穩(wěn)定的)而變化(擴(kuò)展,子類化)。
——《設(shè)計(jì)模式》GoF
策略模式的動(dòng)機(jī)為:在軟件構(gòu)建的過程中,某些對(duì)象使用的算法可能是多種多樣的,經(jīng)常改變,如果將這些算法都編碼到對(duì)象中,將會(huì)使對(duì)象變得異常復(fù)雜;而且有時(shí)候支持不使用的算法也是一種性能負(fù)擔(dān)。
比如當(dāng)前有一種稅種的邏輯處理機(jī)制:

enum TaxBase {
    CN_Tax,
    US_Tax,
    DE_Tax,
};

class SalesOrder{
    TaxBase tax;
public:
    double CalculateTax(){
        //...

        if (tax == CN_Tax){
            //CN***********
        }
        else if (tax == US_Tax){
            //US***********
        }
        else if (tax == DE_Tax){
            //DE***********
        }
        //....
     }

};

若此時(shí)需要增加一種稅種:

enum TaxBase {
    CN_Tax,
    US_Tax,
    DE_Tax,
    FR_Tax       //更改
};

class SalesOrder{
    TaxBase tax;
public:
    double CalculateTax(){
        //...

        if (tax == CN_Tax){
            //CN***********
        }
        else if (tax == US_Tax){
            //US***********
        }
        else if (tax == DE_Tax){
            //DE***********
        }
        else if (tax == FR_Tax){  //更改
            //...
        }

        //....
     }

};

不但需要修改TaxBase中的參數(shù),還需要在SalseOrder中添加關(guān)于新增加稅種的一個(gè)判斷,然后才能進(jìn)行相應(yīng)的計(jì)算。此時(shí)違反了對(duì)開閉原則,同時(shí)也違反了反向依賴的原則。
使用策略模式:

class TaxStrategy{
public:
    virtual double Calculate(const Context& context)=0;
    virtual ~TaxStrategy(){}
};


class CNTax : public TaxStrategy{
public:
    virtual double Calculate(const Context& context){
        //***********
    }
};

class USTax : public TaxStrategy{
public:
    virtual double Calculate(const Context& context){
        //***********
    }
};

class DETax : public TaxStrategy{
public:
    virtual double Calculate(const Context& context){
        //***********
    }
};



class SalesOrder{
private:
    TaxStrategy* strategy;

public:
    SalesOrder(StrategyFactory* strategyFactory){
        this->strategy = strategyFactory->NewStrategy();
    }
    ~SalesOrder(){
        delete this->strategy;
    }

    public double CalculateTax(){
        //...
        Context context();

        double val =
            strategy->Calculate(context); //多態(tài)調(diào)用
        //...
    }

};
此時(shí)再增加一種稅種:
class TaxStrategy{
public:
    virtual double Calculate(const Context& context)=0;
    virtual ~TaxStrategy(){}
};


class CNTax : public TaxStrategy{
public:
    virtual double Calculate(const Context& context){
        //***********
    }
};

class USTax : public TaxStrategy{
public:
    virtual double Calculate(const Context& context){
        //***********
    }
};

class DETax : public TaxStrategy{
public:
    virtual double Calculate(const Context& context){
        //***********
    }
};



//擴(kuò)展
//*********************************
class FRTax : public TaxStrategy{
public:
    virtual double Calculate(const Context& context){
        //.........
    }
};


class SalesOrder{
private:
    TaxStrategy* strategy;

public:
    SalesOrder(StrategyFactory* strategyFactory){
        this->strategy = strategyFactory->NewStrategy();
    }
    ~SalesOrder(){
        delete this->strategy;
    }

    public double CalculateTax(){
        //...
        Context context();

        double val =
            strategy->Calculate(context); //多態(tài)調(diào)用
        //...
    }

};

SalesOrder的CalculateTax函數(shù)多態(tài)調(diào)用了TaxStrategy類中的Calucate方法。而TaxStrategy的每個(gè)子類都是一種新的稅種計(jì)算方式,每個(gè)稅種計(jì)算中還實(shí)現(xiàn)了自己對(duì)應(yīng)的稅種計(jì)算的方法。
在擴(kuò)展的時(shí)候,需要增加TaxStrategy的子類就行了,不需要在改變SalesOrder的中的代碼了。
總結(jié):
<1>Strategy及其子類為組件提供了一系列可重用的算法,從而可以使得類型在運(yùn)行時(shí)方便的根據(jù)需要在各個(gè)算法之間進(jìn)行切換。
<2>Strategy模式提供了用條件判斷語(yǔ)句的另一種選擇,消除條件判斷語(yǔ)句,就是在解耦合。含有許多條件判斷語(yǔ)句的代碼通常都需要Strategy模式。尤其是條件判斷語(yǔ)句在未來會(huì)有增加可能性的時(shí)候,應(yīng)該優(yōu)先考慮Strategy模式。
<3>如果Strategy對(duì)象沒有實(shí)例變量,那么各個(gè)上下文可以共享同一個(gè)Strategy對(duì)象,從而節(jié)省對(duì)象的開銷。
(5)觀察者模式
觀察者模式的動(dòng)機(jī):
在軟件構(gòu)建的過程中,我們需要為某些對(duì)象建立一種“通知依賴關(guān)系”——一個(gè)對(duì)象(目標(biāo)對(duì)象)的狀態(tài)發(fā)生改變,所有的以來對(duì)象(觀察者對(duì)象)都將得到通知。如果這樣的依賴關(guān)系過于緊密,將使得軟件不能很好的抵御變化。
使用面向?qū)ο蠹夹g(shù),可以將這種依賴關(guān)系弱化,并形成一種穩(wěn)定的依賴關(guān)系。從而實(shí)現(xiàn)軟件體系結(jié)構(gòu)的松耦合。
以下是一個(gè)文件分割器的偽碼,其中存在一個(gè)進(jìn)度條:

class FileSplitter
{
    string m_filePath;
    int m_fileNumber;
    ProgressBar* m_progressBar;     // 產(chǎn)生編譯式依賴,實(shí)際是一個(gè)通知

public:
    FileSplitter(const string& filePath, int fileNumber, ProgressBar* progressBar) :
        m_filePath(filePath),
        m_fileNumber(fileNumber),
        m_progressBar(progressBar){

    }

    void split(){

        //1.讀取大文件

        //2.分批次向小文件中寫入
        for (int i = 0; i < m_fileNumber; i++){
            //...
    // 更新進(jìn)度條數(shù)據(jù)
            float progressValue = m_fileNumber;
            progressValue = (i + 1) / progressValue;
            m_progressBar->setValue(progressValue);
        }

    }
};


class MainForm : public Form
{
    TextBox* txtFilePath;
    TextBox* txtFileNumber;
    ProgressBar* progressBar;

public:
    void Button1_Click(){

        string filePath = txtFilePath->getText();
        int number = atoi(txtFileNumber->getText().c_str());

        FileSplitter splitter(filePath, number, progressBar);

        splitter.split();

    }
};

在以上的設(shè)計(jì)方法上,前端界面依賴了一個(gè)ProgressBar,是一種實(shí)現(xiàn)細(xì)節(jié),而實(shí)現(xiàn)細(xì)節(jié)是極易變化的。同時(shí)其中的耦合性也很高,只要需要更改顯示的進(jìn)度的情況,就需要更改前端的界面。
由以上的依賴關(guān)系不難看出,對(duì)于分隔文件的算法相對(duì)來說穩(wěn)定,但是卻依賴了一個(gè)不穩(wěn)定的ProgressBar。
那么如果對(duì)于界面修改的部分來說,他面對(duì)的是一個(gè)通知的數(shù)組,每個(gè)數(shù)組里面都是一個(gè)抽象的通知的接口,就可以使用多態(tài)性來解決解耦的問題。

class IProgress{
public:
    virtual void DoProgress(float value)=0;
    virtual ~IProgress(){}
};


class FileSplitter
{
    string m_filePath;
    int m_fileNumber;

    List<IProgress*>  m_iprogressList; // 抽象通知機(jī)制,支持多個(gè)觀察者

public:
    FileSplitter(const string& filePath, int fileNumber) :
        m_filePath(filePath),
        m_fileNumber(fileNumber){

    }


    void split(){

        //1.讀取大文件

        //2.分批次向小文件中寫入
        for (int i = 0; i < m_fileNumber; i++){
            //...

            float progressValue = m_fileNumber;
            progressValue = (i + 1) / progressValue;
            onProgress(progressValue);//發(fā)送通知
        }

    }


    void addIProgress(IProgress* iprogress){
        m_iprogressList.push_back(iprogress);
    }

    void removeIProgress(IProgress* iprogress){
        m_iprogressList.remove(iprogress);
    }


protected:
    virtual void onProgress(float value){

        List<IProgress*>::iterator itor=m_iprogressList.begin();

        while (itor != m_iprogressList.end() )
            (*itor)->DoProgress(value); //更新進(jìn)度條
            itor++;
        }
    }
};



class MainForm : public Form, public IProgress(抽象基類)
{
    TextBox* txtFilePath;
    TextBox* txtFileNumber;

    ProgressBar* progressBar;

public:
    void Button1_Click(){

        string filePath = txtFilePath->getText();
        int number = atoi(txtFileNumber->getText().c_str());

        ConsoleNotifier cn;

        FileSplitter splitter(filePath, number);

        splitter.addIProgress(this); //訂閱通知
        splitter.addIProgress(&cn); //訂閱通知

        splitter.split();

        splitter.removeIProgress(this);

    }

    virtual void DoProgress(float value){
        progressBar->setValue(value);
    }
};

class ConsoleNotifier : public IProgress {
public:
    virtual void DoProgress(float value){
        cout << ".";
    }
};

對(duì)于這個(gè)解決方案來說,將ProgressBar進(jìn)行了抽象,對(duì)于FileSplitter只需要依賴一個(gè)list,由一個(gè)list來維護(hù)IProgressBar* 的列表,對(duì)于每個(gè)ProgressBar控件來說,只需要繼承IProgressBar即可。
對(duì)于MainForm來說,只需要調(diào)用比較穩(wěn)定的FileSplitter既可以來調(diào)用其中的分隔文件的方法,而在FileSplitter中已經(jīng)實(shí)現(xiàn)了遍歷list中所有IProgressBar并通知的功能,因此實(shí)現(xiàn)了觀察者模式的要求。
要點(diǎn)總結(jié):
使用面向?qū)ο蟮某橄?,Observer模式使得我們可以獨(dú)立地改變目標(biāo)與觀察者,從而使二者之間的依賴關(guān)系達(dá)到松耦合。目標(biāo)發(fā)送通知時(shí),無需制定觀察者,通知(可以攜帶通知信息作為參數(shù))會(huì)自動(dòng)傳播。觀察者自己決定是否需要訂閱通知,目標(biāo)對(duì)象對(duì)此一無所知。Observer模式是基于事件的UI框架中非常常用的設(shè)計(jì)模式,也是MVC模式的一個(gè)重要組成部分。
(6)裝飾模式
動(dòng)態(tài)(組合)地給一個(gè)對(duì)象增加一些額外的指責(zé)。就增加功能而言,Decorator模式比生成子類(繼承)更為靈活(消除重復(fù)代碼、減少子類個(gè)數(shù))。
——《設(shè)計(jì)模式》GoF
裝飾模式動(dòng)機(jī):
在某些情況下,我們可能會(huì)“過度的使用繼承來擴(kuò)展對(duì)象的功能”,由于繼承為類型引入靜態(tài)特質(zhì),使得這種擴(kuò)展方式缺乏靈活性;并且隨著子類的增多(擴(kuò)展功能的在增多),各種子類的組合(擴(kuò)展功能的組合)會(huì)導(dǎo)致子類的膨脹。
在下面有一組偽代碼,這組偽代碼主要描述一些流的操作,比如文件流、網(wǎng)絡(luò)流、內(nèi)存流等,如果我們對(duì)于流提出更多的要求,比如需要對(duì)文件流加密、對(duì)網(wǎng)絡(luò)流加密以及內(nèi)存流的加密,同時(shí)如果考慮緩沖的情況下,那么這些流都可能會(huì)要緩沖,也可能繼續(xù)的組合,產(chǎn)生加密緩沖網(wǎng)絡(luò)流等等的操作。

//業(yè)務(wù)操作
class Stream{
public:
    virtual char Read(int number)=0;
    virtual void Seek(int position)=0;
    virtual void Write(char data)=0;

    virtual ~Stream(){}
};

//主體類
class FileStream: public Stream{
public:
    virtual char Read(int number){
        //讀文件流
    }
    virtual void Seek(int position){
        //定位文件流
    }
    virtual void Write(char data){
        //寫文件流
    }

};

class NetworkStream :public Stream{
public:
    virtual char Read(int number){
        //讀網(wǎng)絡(luò)流
    }
    virtual void Seek(int position){
        //定位網(wǎng)絡(luò)流
    }
    virtual void Write(char data){
        //寫網(wǎng)絡(luò)流
    }

};

class MemoryStream :public Stream{
public:
    virtual char Read(int number){
        //讀內(nèi)存流
    }
    virtual void Seek(int position){
        //定位內(nèi)存流
    }
    virtual void Write(char data){
        //寫內(nèi)存流
    }

};

//擴(kuò)展操作
class CryptoFileStream :public FileStream{
public:
    virtual char Read(int number){

        //額外的加密操作...
        FileStream::Read(number);//讀文件流

    }
    virtual void Seek(int position){
        //額外的加密操作...
        FileStream::Seek(position);//定位文件流
        //額外的加密操作...
    }
    virtual void Write(byte data){
        //額外的加密操作...
        FileStream::Write(data);//寫文件流
        //額外的加密操作...
    }
};

class CryptoNetworkStream : :public NetworkStream{
public:
    virtual char Read(int number){

        //額外的加密操作...
        NetworkStream::Read(number);//讀網(wǎng)絡(luò)流
    }
    virtual void Seek(int position){
        //額外的加密操作...
        NetworkStream::Seek(position);//定位網(wǎng)絡(luò)流
        //額外的加密操作...
    }
    virtual void Write(byte data){
        //額外的加密操作...
        NetworkStream::Write(data);//寫網(wǎng)絡(luò)流
        //額外的加密操作...
    }
};

class CryptoMemoryStream : public MemoryStream{
public:
    virtual char Read(int number){

        //額外的加密操作...
        MemoryStream::Read(number);//讀內(nèi)存流
    }
    virtual void Seek(int position){
        //額外的加密操作...
        MemoryStream::Seek(position);//定位內(nèi)存流
        //額外的加密操作...
    }
    virtual void Write(byte data){
        //額外的加密操作...
        MemoryStream::Write(data);//寫內(nèi)存流
        //額外的加密操作...
    }
};

class BufferedFileStream : public FileStream{
    //...
};

class BufferedNetworkStream : public NetworkStream{
    //...
};

class BufferedMemoryStream : public MemoryStream{
    //...
}




class CryptoBufferedFileStream :public FileStream{
public:
    virtual char Read(int number){

        //額外的加密操作...
        //額外的緩沖操作...
        FileStream::Read(number);//讀文件流
    }
    virtual void Seek(int position){
        //額外的加密操作...
        //額外的緩沖操作...
        FileStream::Seek(position);//定位文件流
        //額外的加密操作...
        //額外的緩沖操作...
    }
    virtual void Write(byte data){
        //額外的加密操作...
        //額外的緩沖操作...
        FileStream::Write(data);//寫文件流
        //額外的加密操作...
        //額外的緩沖操作...
    }
};



void Process(){

        //編譯時(shí)裝配
    CryptoFileStream *fs1 = new CryptoFileStream();

    BufferedFileStream *fs2 = new BufferedFileStream();

    CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream();

}

上述代碼的復(fù)用性不高,對(duì)于每種方式都有大量的重復(fù)代碼。這樣的設(shè)計(jì)會(huì)導(dǎo)致class的數(shù)量激增。


如果把那些因?yàn)榻M合而產(chǎn)生的class(比如CryptoFileStream)的繼承關(guān)系先打斷,而是改為使用內(nèi)部持有原先父類的指針,比如CryptoFileStream原先繼承了FileStream,改為持有FileStream。這時(shí)候原class中的調(diào)用父類的函數(shù)全都可以改為使用自己持有的指針對(duì)象來調(diào)用。此時(shí)代碼的重復(fù)性大幅度提高,只是每一個(gè)組合型的class中持有的那個(gè)指針不屬于同一個(gè)(比如,持有的為FileStream,NetworkStream以及BufferStream的指針),如果觀察一下,也不難發(fā)現(xiàn),其實(shí)FileStream,NetworkStream以及BufferStream又都有同一個(gè)父類Stream,所以利用多態(tài)性,就可以將每個(gè)組合類型中的持有的指針變量,聲明為他們共同的父類,利用多態(tài)性只需要讓父類的指向子類即可(Stream stream = new FileStream();),這就實(shí)現(xiàn)了,編譯時(shí)都為一樣的,在運(yùn)行時(shí)提供不同的對(duì)象。此時(shí)會(huì)發(fā)現(xiàn)具有大量重復(fù)的類,合并了同類項(xiàng),減少很多不必要的組合類。

//業(yè)務(wù)操作
class Stream{

public:
    virtual char Read(int number)=0;
    virtual void Seek(int position)=0;
    virtual void Write(char data)=0;

    virtual ~Stream(){}
};

//主體類
class FileStream: public Stream{
public:
    virtual char Read(int number){
        //讀文件流
    }
    virtual void Seek(int position){
        //定位文件流
    }
    virtual void Write(char data){
        //寫文件流
    }

};

class NetworkStream :public Stream{
public:
    virtual char Read(int number){
        //讀網(wǎng)絡(luò)流
    }
    virtual void Seek(int position){
        //定位網(wǎng)絡(luò)流
    }
    virtual void Write(char data){
        //寫網(wǎng)絡(luò)流
    }

};

class MemoryStream :public Stream{
public:
    virtual char Read(int number){
        //讀內(nèi)存流
    }
    virtual void Seek(int position){
        //定位內(nèi)存流
    }
    virtual void Write(char data){
        //寫內(nèi)存流
    }

};

//擴(kuò)展操作


class CryptoStream: public Stream {

    Stream* stream;//...

public:
    CryptoStream(Stream* stm):stream(stm){

    }


    virtual char Read(int number){

        //額外的加密操作...
        stream->Read(number);//讀文件流
    }
    virtual void Seek(int position){
        //額外的加密操作...
        stream::Seek(position);//定位文件流
        //額外的加密操作...
    }
    virtual void Write(byte data){
        //額外的加密操作...
        stream::Write(data);//寫文件流
        //額外的加密操作...
    }
};



class BufferedStream : public Stream{

    Stream* stream;//...

public:
    BufferedStream(Stream* stm):stream(stm){

    }
    //...
};





void Process(){

    //運(yùn)行時(shí)裝配
    FileStream* s1=new FileStream();
    CryptoStream* s2=new CryptoStream(s1);

    BufferedStream* s3=new BufferedStream(s1);

    BufferedStream* s4=new BufferedStream(s2);



}

觀察上面的代碼來說,其實(shí)有很多class里面都持有同樣的變量stream,那么這些類可以向上再抽象一個(gè)父類,將strem 在父類中聲明。
//業(yè)務(wù)操作
class Stream{

public:
    virtual char Read(int number)=0;
    virtual void Seek(int position)=0;
    virtual void Write(char data)=0;

    virtual ~Stream(){}
};

//主體類
class FileStream: public Stream{
public:
    virtual char Read(int number){
        //讀文件流
    }
    virtual void Seek(int position){
        //定位文件流
    }
    virtual void Write(char data){
        //寫文件流
    }

};

class NetworkStream :public Stream{
public:
    virtual char Read(int number){
        //讀網(wǎng)絡(luò)流
    }
    virtual void Seek(int position){
        //定位網(wǎng)絡(luò)流
    }
    virtual void Write(char data){
        //寫網(wǎng)絡(luò)流
    }

};

class MemoryStream :public Stream{
public:
    virtual char Read(int number){
        //讀內(nèi)存流
    }
    virtual void Seek(int position){
        //定位內(nèi)存流
    }
    virtual void Write(char data){
        //寫內(nèi)存流
    }

};

//擴(kuò)展操作

DecoratorStream: public Stream{
protected:
    Stream* stream;//...

    DecoratorStream(Stream * stm):stream(stm){

    }

};

class CryptoStream: public DecoratorStream {

public:
    CryptoStream(Stream* stm):DecoratorStream(stm){

    }


    virtual char Read(int number){

        //額外的加密操作...
        stream->Read(number);//讀文件流
    }
    virtual void Seek(int position){
        //額外的加密操作...
        stream::Seek(position);//定位文件流
        //額外的加密操作...
    }
    virtual void Write(byte data){
        //額外的加密操作...
        stream::Write(data);//寫文件流
        //額外的加密操作...
    }
};

class BufferedStream : public DecoratorStream{

    Stream* stream;//...

public:
    BufferedStream(Stream* stm):DecoratorStream(stm){

    }
    //...
};
void Process(){
    //運(yùn)行時(shí)裝配
    FileStream* s1=new FileStream();

    CryptoStream* s2=new CryptoStream(s1);

    BufferedStream* s3=new BufferedStream(s1);

    BufferedStream* s4=new BufferedStream(s2);
}

FileStream、NetworkStream還有BufferStream可以獨(dú)立執(zhí)行任務(wù),而加密和緩存其實(shí)就是對(duì)他們的增強(qiáng)。只需要對(duì)Stream(這三個(gè)類的共同父類)增加加密功能、增加緩沖的功能,而在使用的時(shí)候自由組合即可,而不需要把代碼全都寫出來。
總結(jié):
<1>通過采用組合而非繼承的手法,Decorator模式實(shí)現(xiàn)了在運(yùn)動(dòng)時(shí)動(dòng)態(tài)擴(kuò)展對(duì)象功能的能力,而且可以根據(jù)需要擴(kuò)展多個(gè)功能。避免了使用集成帶來的“靈活性差”和“多子類衍生的問題”。
<2>Decorator類在接口上變現(xiàn)為is-a Component的繼承關(guān)系,即Decorator類集成了Component類所具有的接口。但在實(shí)現(xiàn)上又表現(xiàn)為has-a Component的組合關(guān)系,即Decorator類又使用了另外一個(gè)Component類。
<3>Decorator模式的目的并非解決“多子類衍生的多繼承”問題,Decorator模式應(yīng)用的重要點(diǎn)在于解決“主體類在多個(gè)方向上的擴(kuò)展功能”——是為“裝飾”的含義。
(7)橋模式
將抽象部分(業(yè)務(wù)功能)與實(shí)現(xiàn)部分(平臺(tái)實(shí)現(xiàn))分離,是他們都可以獨(dú)立地變化。
——《設(shè)計(jì)模式》GoF

橋模式的動(dòng)機(jī):
由于某些類型的固有的實(shí)現(xiàn)邏輯,使得它們具有兩個(gè)變化的維度,乃至多個(gè)維度的變化。

假設(shè)有如下的通信的偽碼描述包括登陸、發(fā)信息、播放音樂等等的功能,同時(shí)也支持不同的平臺(tái)的支持,比如PC、移動(dòng)端等,其中版本也具有不同的功能,比如精簡(jiǎn)版和完整版。

class Messager{
public:
    virtual void Login(string username, string password)=0;
    virtual void SendMessage(string message)=0;
    virtual void SendPicture(Image image)=0;

    virtual void PlaySound()=0;
    virtual void DrawShape()=0;
    virtual void WriteText()=0;
    virtual void Connect()=0;

    virtual ~Messager(){}
};

//平臺(tái)實(shí)現(xiàn)

class PCMessagerBase : public Messager{
public:

    virtual void PlaySound(){
        //**********
    }
    virtual void DrawShape(){
        //**********
    }
    virtual void WriteText(){
        //**********
    }
    virtual void Connect(){
        //**********
    }
};

class MobileMessagerBase : public Messager{
public:

    virtual void PlaySound(){
        //==========
    }
    virtual void DrawShape(){
        //==========
    }
    virtual void WriteText(){
        //==========
    }
    virtual void Connect(){
        //==========
    }
};

//業(yè)務(wù)抽象

class PCMessagerLite : public PCMessagerBase {
public:

    virtual void Login(string username, string password){

        PCMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message){

        PCMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){

        PCMessagerBase::DrawShape();
        //........
    }
};

class PCMessagerPerfect : public PCMessagerBase {
public:

    virtual void Login(string username, string password){

        PCMessagerBase::PlaySound();
        //********
        PCMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message){

        PCMessagerBase::PlaySound();
        //********
        PCMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){

        PCMessagerBase::PlaySound();
        //********
        PCMessagerBase::DrawShape();
        //........
    }
};

class MobileMessagerLite : public MobileMessagerBase {
public:

    virtual void Login(string username, string password){

        MobileMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message){

        MobileMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){

        MobileMessagerBase::DrawShape();
        //........
    }
};

class MobileMessagerPerfect : public MobileMessagerBase {
public:

    virtual void Login(string username, string password){

        MobileMessagerBase::PlaySound();
        //********
        MobileMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message){

        MobileMessagerBase::PlaySound();
        //********
        MobileMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){

        MobileMessagerBase::PlaySound();
        //********
        MobileMessagerBase::DrawShape();
        //........
    }
};

void Process(){
        //編譯時(shí)裝配
        Messager *m =
            new MobileMessagerPerfect();
}

對(duì)于以上那個(gè)代碼來說,組合的過程中會(huì)存在大量的重復(fù)代碼,同時(shí)在組合的過程中,會(huì)產(chǎn)生大量的類的組合而產(chǎn)生的類。
依靠Decorator的經(jīng)驗(yàn),把大量的繼承改編為持有原父類的指針變量,并且可以聲明為共同的父類,利用多態(tài)性將靜態(tài)綁定改為動(dòng)態(tài)綁定。
并且觀察發(fā)現(xiàn),對(duì)于Message類的功能應(yīng)該拆分開。

class Messager{
protected:
     MessagerImp* messagerImp;//...
public:
    virtual void Login(string username, string password)=0;
    virtual void SendMessage(string message)=0;
    virtual void SendPicture(Image image)=0;

    virtual ~Messager(){}
};

class MessagerImp{
public:
    virtual void PlaySound()=0;
    virtual void DrawShape()=0;
    virtual void WriteText()=0;
    virtual void Connect()=0;

    virtual MessagerImp(){}
};

//平臺(tái)實(shí)現(xiàn) n
class PCMessagerImp : public MessagerImp{
public:

    virtual void PlaySound(){
        //**********
    }
    virtual void DrawShape(){
        //**********
    }
    virtual void WriteText(){
        //**********
    }
    virtual void Connect(){
        //**********
    }
};
class MobileMessagerImp : public MessagerImp{
public:

    virtual void PlaySound(){
        //==========
    }
    virtual void DrawShape(){
        //==========
    }
    virtual void WriteText(){
        //==========
    }
    virtual void Connect(){
        //==========
    }
};

//業(yè)務(wù)抽象 m

//類的數(shù)目:1+n+m

class MessagerLite :public Messager {


public:

    virtual void Login(string username, string password){

        messagerImp->Connect();
        //........
    }
    virtual void SendMessage(string message){

        messagerImp->WriteText();
        //........
    }
    virtual void SendPicture(Image image){

        messagerImp->DrawShape();
        //........
    }
};

class MessagerPerfect  :public Messager {
public:

    virtual void Login(string username, string password){

        messagerImp->PlaySound();
        //********
        messagerImp->Connect();
        //........
    }
    virtual void SendMessage(string message){

        messagerImp->PlaySound();
        //********
        messagerImp->WriteText();
        //........
    }
    virtual void SendPicture(Image image){

        messagerImp->PlaySound();
        //********
        messagerImp->DrawShape();
        //........
    }
};

void Process(){
    //運(yùn)行時(shí)裝配
    MessagerImp* mImp=new PCMessagerImp();
    Messager *m =new Messager(mImp);
}

要點(diǎn)總結(jié):
Bridge模式使用“對(duì)象間的組合關(guān)系”解耦了抽象和實(shí)現(xiàn)之間固有的綁定關(guān)系,使得抽象和實(shí)現(xiàn)可以沿著各自的維度來變化。所謂抽象和實(shí)現(xiàn)沿著各自維度的變化,即“子類化”他們。
Bridge模式有時(shí)候類似于多繼承方案,但是多繼承方案往往違背單一指責(zé)原則(即一個(gè)類只有一個(gè)變化的原因),復(fù)用性較差。Bridge模式是比多繼承方案更好的解決方法。
Bridge模式的應(yīng)用一般在“兩個(gè)非常強(qiáng)的變化維度”,有時(shí)一個(gè)類也有多于兩個(gè)的變化維度,這是可以使用Bridge的擴(kuò)展模式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本周課程主要內(nèi)容為:C++設(shè)計(jì)模式簡(jiǎn)介、面向?qū)ο笤O(shè)計(jì)八大原則和DOF-23設(shè)計(jì)模式中的5中模式,包括templat...
    cayhw閱讀 580評(píng)論 0 3
  • 本周內(nèi)容(1)設(shè)計(jì)模式簡(jiǎn)介(2)面向?qū)ο笤O(shè)計(jì)原則(3)模板方法(4)策略模式(5)觀察者模式(6)裝飾模式(7)橋...
    小小出大炮閱讀 990評(píng)論 0 2
  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 4,073評(píng)論 1 15
  • 設(shè)計(jì)模式簡(jiǎn)介: 什么是設(shè)計(jì)模式?每一個(gè)模式描述了一個(gè)在我們周圍不斷重復(fù)發(fā)生的問題,以及該問題的解決方案的核心。這樣...
    hjsadam閱讀 400評(píng)論 0 2
  • 設(shè)計(jì)模式基本原則 開放-封閉原則(OCP),是說軟件實(shí)體(類、模塊、函數(shù)等等)應(yīng)該可以拓展,但是不可修改。開-閉原...
    西山薄涼閱讀 4,071評(píng)論 3 14

友情鏈接更多精彩內(nèi)容