C++装饰模式

参考:设计模式C++实现(11)——装饰模式 

1. 首先如果有一台手机,并且你一直在不停的换手机,那么可以关于手机抽象一个类(person),在用具体的手机(iphone)去继承,并通过多态转换(iphone*转person*),从而可以调用统一的抽象接口(phoneTmp->showLook()),这样无论,你换哪个手机,调用的方法(phoneTmp->showLook())都不会变,只会增加代码,不会修改代码。完美符合开闭原则。


#include <stdlib.h>
#include <iostream>

class person {
public:
    virtual void showLook() = 0;
};

class iphone : public person {
public:
    virtual void showLook() {
        printf("iphone 裸机\r\n");
    }
private:
    person *mPhone;
};

int main() {
    person *phoneTmp = new iphone;
    phoneTmp->showLook();
}

2. 好现在买回来了手机你想给手机贴个膜。于是你的代码可能会变为下面这样。通过继承且在子类添加方法


#include <stdlib.h>
#include <iostream>
#include <string>

class phone {
public:
    virtual void showLook() = 0;
};

class iphone : public phone {
public:
    iphone(std::string nm) {
        name = nm;
    };
    virtual void showLook() {
        printf("%s\r\n", name.c_str());
    }
private:
    phone *mPhone;
    std::string name;
};

class DecorateMo : public phone {
public:
    DecorateMo(phone* mp) {
        mPhone1 = mp;
    };
    virtual void showLook() {
        mPhone1->showLook();
        addMo();
    }

private:
    phone* mPhone1;
    void addMo() {
        printf(" 贴膜\r\n");   
    };
};


int main() {
    phone* tmp = new DecorateMo(new iphone("XXX的iphone"));
    tmp->showLook();
}

3.这么看单独没啥问题,但过了几天你又要装个手机壳,同上,你又要去继承写一个类DecorateKe, 一样的步骤,实现出来大概是这样的。


#include <stdlib.h>
#include <iostream>
#include <string>

class person {
public:
    virtual void showLook() = 0;
};

class iphone : public person {
public:
    iphone(std::string nm) {
        name = nm;
    };
    virtual void showLook() {
        printf("%s ", name.c_str());
    }
private:
    person *mPhone;
    std::string name;
};

class DecorateMo : public person {
public:
    DecorateMo(person* mp) {
        mPhone1 = mp;
    };
    virtual void showLook() {
        mPhone1->showLook();
        addMo();
    }

private:
    person* mPhone1;
    void addMo() {
        printf("贴膜\r\n");   
    };
};

class DecorateKe : public person {
public:
    DecorateKe(person* mp) {
        mPhone1 = mp;
    };
    virtual void showLook() {
        mPhone1->showLook();
        addKe();
    }

private:
    person* mPhone1;
    void addKe() {
        printf("装手机壳\r\n");   
    };
};

int main() {
    person* tmp = new DecorateMo(new iphone("XXX的iphone"));
    // tmp->showLook();
    DecorateKe *tmp1 = new DecorateKe(tmp);
    tmp1->showLook();
}

4. 所以为什么选择装饰模式,不用继承呢?

比如现在需要算配件:手机壳,膜一共需要多少钱?那么如果用现有的结构,就需要在person类加一个cost()方法,在相关类加方法,在加个全局变量,写出来大概是这样的。


#include <stdlib.h>
#include <iostream>
#include <string>
    int total;

class person {
   public:
    virtual void showLook() = 0;
    virtual void cost() {
        printf("一共用了%d元\r\n", total);
    };

};

class iphone : public person {
public:
    iphone(std::string nm) {
        name = nm;
    };
    virtual void showLook() {
        printf("%s ", name.c_str());
    }
private:
    person *mPhone;
    std::string name;
};


class DecorateMo : public person {
public:
    DecorateMo(person* mp) {
        mPhone1 = mp;
    };
    virtual void showLook() {
        mPhone1->showLook();
        addMo();
    }

private:
    person* mPhone1;
    void addMo() {
        printf("贴膜\r\n");
        cost();
    };
    virtual void cost() {
        total += 20;
        printf("20\r\n");
    }
};

class DecorateKe : public person {
public:
    DecorateKe(person* mp) {
        mPhone1 = mp;
    };
    virtual void showLook() {
        mPhone1->showLook();
        addKe();
    }

private:
    person* mPhone1;
    void addKe() {
        printf("装手机壳\r\n");
        cost();
    };
    virtual void cost() {
        total += 30;
        printf("30\r\n");
    }
};

int main() {
    person *tmpPhone = new iphone("XXX的iphone");
    person *tmp = new DecorateMo(tmpPhone);
    // tmp->showLook();
    person *tmp1 = new DecorateKe(tmp);
    tmp1->showLook();
    tmpPhone->cost();
}

5. 或许有其他办法,但没有可以不修改person类的办法实现这个功能。。因为很多东西继承peson,改动person如果逻辑复杂了,会带来新风险,这样很不好,我们希望哪个person类不要被修改,所以装饰模式的作用就体现出来了。

装饰模式的优点:就是便于扩展,并且不修改基类。且扩展性非常好,不用修改任何原有代码。

那么用装饰模式怎么增加cost功能呢?首先加个抽象的Decorate类,但为了保持之前的showLook()方法,我们需要用Decorate类继承person类。

  • 感觉区别不是很大,但不修改person类会直接做到对person类和他的子类的BUG的0输入。。
  • 并且这种模式可以增加任意的新功能,且可以保持不修改之前的person类相关代码,这个特性完全符合了开闭思想,降低风险的引入。

#include <stdlib.h>
#include <iostream>
#include <string>
    int total;

class person {
   public:
    virtual void showLook() = 0;
};

class iphone : public person {
public:
    iphone(std::string nm) {
        name = nm;
    };
    virtual void showLook() {
        printf("%s\r\n", name.c_str());
    }
private:
    person *mPhone;
    std::string name;
};
class Decorate : public person{
public:
    virtual void cost() {
        printf("total = %d\r\n", total);
    };
    virtual void showLook() = 0;
};


class DecorateMo : public Decorate {
   public:
    DecorateMo(person* mp) {
        mPhone1 = mp;
    };
    virtual void showLook() {
        addMo();
    }
    void costTotal() { Decorate::cost(); }

   private:
    person* mPhone1;
    void addMo() {
        printf("贴膜\r\n");
        cost();
    };
    virtual void cost() {
        total += 20;
        printf("20\r\n");
    }
};

class DecorateKe : public Decorate {
public:
    DecorateKe(person* mp) {
        mPhone1 = mp;
    };
    virtual void showLook() {
        addKe();
    }
    void costTotal() {Decorate::cost(); }

private:
    person* mPhone1;
    void addKe() {
        printf("装手机壳\r\n");
        cost();
    };
    virtual void cost() {
        total += 30;
        printf("30\r\n");
    }
};
int main() {
    person *tmpPhone = new iphone("XXX的iphone");
    tmpPhone->showLook();

    DecorateMo *tmp = new DecorateMo(tmpPhone);
    tmp->showLook();
    tmp->costTotal();
    person *tmp1 = dynamic_cast<person *>(tmp);

    DecorateKe *tmp2 = new DecorateKe(tmp1);
    tmp2->showLook();
    tmp2->costTotal();

}

总结: 装饰者模式适用于:新增加的功能点,在不改变现有的子类,父类的前提下,抽象一个接口类(Decarate),说白了,为了不改变原有的基类,加了一个中间人Decarate去继承Person,这样新加的功能,与之前的代码完全解耦。所以当需要新加一个功能点的时候,考虑用装饰者模式吧。

Guess you like

Origin blog.csdn.net/m0_37844072/article/details/120582898