//基类抽象类。 class Light { public: Light(){ } public: virtual string GetInfo() = 0; }; //基本单元BulbLight,电灯泡,它们后期可能需要扩展新的功能 class BulbLight : public Light { public: BulbLight(){ } publc: string GetInfo(){ return "BulbLight"; } }; //基本单元LampLight,白炽灯 class LampLight : public Light { public: LampLight(){ } publc: string GetInfo(){ return "LampLight"; } }; //以下为装饰因子 //添加带颜色的功能 class ColorLight : public Light { public: ColorLight(Light* light){ this->m_pLight = light; } public: string GetInfo(){ return m_pLight->GetInfo() + " Color"; } private: Light* m_pLight; }; //添加会闪烁的功能 class BlinkLight : public Light { public: BlinkLight(Light* light){ this->m_pLight = light; } public: string GetInfo(){ return m_pLight->GetInfo() + " Blink"; } } //添加声控功能 class VoiceLight : public Light { public: VoiceLight(Light* light){ this->m_pLight = light; } public: string GetInfo(){ return m_pLight->GetInfo() + " Voice"; } } //通过将基本单元和装饰因子组合,可以形成功能强大的灯。 //例如创建一个闪烁又具有声控的电灯泡 BulbLight* pLight = new BulbLight; BlinkLight* pBlink = new BlinkLight(pLight); VoiceLight* pVoiceBlink = new VoiceLight(pBlink); cout<<pVoiceBlink->GetInfo()<<endl;
装饰者模式正如它的名字一样, 就是一个装饰而已。它主要是用于给现有的类增加一些新的功能,同时又不影响原有的接口。通常做法是有一个抽象类,例如上文的Light,它就是一个灯的抽象,它有一个功能叫做GetInfo,由于它是抽象的,故而一般像这样的方法也会定义为虚函数,便于子类进行自己的个性化。BulbLight和LampLight是具体的灯类,它有自己的GetInfo功能,但是现在需求增加了,需要声控的BulbLight灯。对于此,我们有两个方案,一个通过继承于BulbLight,添加声控功能同时再调用BulbLight的基本的GetInfo方法不就可以了,一个是通过上面的类似的方法,继承于抽象的Light,将要添加声控功能的BulbLight灯传递进去,同时保持GetInfo接口不变,通过组合复用原有功能并添加了新功能。
继承与组合的优缺点:如果你想要创建一个闪烁并具有声控功能的BulbLight灯,你就会发现继承有它的局限性。通过组合,你可以任意给现有的已有的功能增加任意想要添加的功能,而且是可以无限制的扩展。通过继承,当你只增加一个功能,你还看不出问题来,毕竟一个继承就搞定,当功能逐渐添加,你的类的继承层次会逐渐增大,而且当同时需要具有a和b和c的功能的时候,难道还需要多重继承?越往后扩展越被限制。这个明显违背了对扩展开放的原则。
此种模式通过组合完成它的任务,为了维护代码的接口统一性,将新增功能的类同样继承于Light抽象类,并实现其相关接口;为了给现有的类增加功能,将现有的类的对象传递进去,通过组合委托的方式调用其旧的功能,可以在其后添加新的代码增加新的功能。
Class Abstract { virtual void BasicFunction() = 0;//基本功能 }; Class OldConcrete : public Abstract { void BasicFunction(){//基本功能 //....过去的基本功能代码 } }; //过去的使用的地方: Abstract* pConcrete = new OldConcrete(); pConcrete.BasicFunction(); //现在来了新需求,需要扩展现有功能。 Class NewConcrete : public Abstract //为了保持接口统一,面向接口编程,继承于抽象基类 { public: NewConcrete(Abstract* pA){ this.m_pA = pA; } public: void BasicFunction(){ //1,假设需要先调用过去的旧功能 m_pA->BasicFunction(); //2, 调用扩展的新功能,通过这种方式就透明的增加了新的功能 ExtendFunction(); } private: void ExtendFunction(){ //这个是扩展的新功能的代码 } private: Abstract* m_pA; }; //现在的使用的地方: Abstract* pConcrete = new OldConcrete(); Abstract* pNewConcrete = NewConcrete(pConcrete);//只需要再包装一次而已 pNewConcrete.BasicFunction();//接口不要有任何变化,旧的OldConcrete没有任何变化,就偷偷的添加了新的功能。