设计模式学习(三) 装饰模式

本文介绍装饰模式的概念及其实现,通过实例展示如何动态给对象增加职责,提高代码灵活性。使用C++实现,包括产品类、装饰类及具体装饰类,通过递归调用实现功能叠加。


这是个人学习编程模式的系列学习笔记第三篇。
采用Qt Creator进行编写,但尽量采用C++基础语法。
装饰模式(Decorator):动态地给一个对象增加一些额外的职责,就增加的功能来说,Decorator模式相比生成子类更加灵活。
个人理解,就是定义一个抽象类功能接口,然后具体类和装饰类都继承这个接口。装饰类通过继承实现不同的装饰功能子类,通过对具体类装饰不同的装饰功能子类实现对具体类的功能增加。具体的实现机制是在装饰类中添加抽象类指针,在装饰功能子类运行新增的功能,然后调用父类(装饰类)的功能接口去调用具体类的功能接口。有点类似递归,可以对具体类装饰多个装饰功能类,最后统一执行(执行顺序取决于装饰功能类是先运行新功能还是先调用原有功能)。
描述有点抽象。具体类之间的关系可以参考下面的UML图。

场景描述

有一家所谓的保健品生成厂家,生产各种保健品(比如大力丸、磁力杯等),由于是保健品,具体的功能就根据需要来定了,反正就是这么一说,按需要,啥病都能治。需要啥疗效就增加啥疗效说明,方便快捷(比如延迟寿命、治疗高血压、治疗癌症等)。按需生产,需要啥样功能的产品定制生产。

设计思路

定义一个产品类(Product,抽象类)作为疗效接口。
具体的产品类继承这个产品类。并实现这个疗效的接口(只实现基础的产品说明,还没添加疗效说明)。
定义一个添加疗效说明的装饰类,这个类(Addeffect,抽象类)也继承产品类,并实现这个疗效的接口(这个接口实现对具体产品类的疗效接口的调用,用于添加具体的产品说明。因此这个类需要有一个属性是指向产品类(抽象类)的指针,用于指示调用具体什么产品的产品说明)。
具体的装饰类用于添加具体的疗效说明,然后调用父类的疗效功能接口添加基础产品说明。
可以根据需求对产品定制(装饰)不同的功能装饰类(添加不同的疗效说明)。
这个样式可以有变化,比如只有一种保健产品,只是根据需求定制不同的疗效说明,则产品类可以不是抽象类,直接实现具体的产品功能(就没有子类继承了)。
还定义了一个工厂类,用于创建不同的产品和添加不同的疗效说明(简单工厂模式)。
为实验前后端分离,定义了客户端类,用于处理用户输入(产品类型、品牌、疗效)和输出(最终按需定制的保健品)。

UML图

在这里插入图片描述

代码

由于添加了客户端和工厂类,在主程序中还添加了简单的用户输入错误处理,代码略微复杂。但这部分同装饰模式没有什么关系,如果只是试验装饰模式,可以不用Factory类和Client类。在main.cpp中通过以下简单代码来测试:

#include "product.h"
#include "daliwan.h"
#include "cilibei.h"
#include "addeffect.h"
#include "prolonglife.h"
#include "treatingcancer.h"
#include "treatingthypertension.h"

#include <iostream>

using namespace std;
int main()
{
    Product* product = new DaLiWan("智商税");
    AddEffect* longlife = new ProlongLife(product);
    AddEffect* cancer = new TreatingCancer(longlife);
    AddEffect* hypertension = new TreatingtHypertension(cancer);
    hypertension->effect();
    delete hypertension;
    delete cancer;
    delete longlife;
    delete product;

    return 0;
}

以下为完整代码:
Product类
product.h

class Product
{
public:
    Product();
    ~Product();

    virtual void effect() = 0;

};

product.cpp

Product::Product()
{

}

Product::~Product()
{

}

DaLiWan类
daliwan.h

#include "product.h"
#include <iostream>

using namespace std;

class DaLiWan : public Product
{
private:
    string m_brand;
public:
    DaLiWan(string name);
    ~DaLiWan();

    void effect();
};

daliwan.cpp

#include "daliwan.h"
#include <iostream>

using namespace std;

DaLiWan::DaLiWan(string name)
{
    m_brand = name;
}

DaLiWan::~DaLiWan()
{

}

void DaLiWan::effect()
{
    cout<<m_brand<<"牌大力丸是一个淀粉做的药丸,它能:";
}

CiLiBei类
cilibei.h

#include "product.h"
#include <iostream>

using namespace std;

class CiLiBei : public Product
{
private:
    string m_brand;
public:
    CiLiBei(string name);
    ~CiLiBei();

    void effect();
};

cilibei.cpp

#include "cilibei.h"
#include <iostream>

using namespace std;

CiLiBei::CiLiBei(string name)
{
    m_brand = name;
}

CiLiBei::~CiLiBei()
{

}

void CiLiBei::effect()
{
    cout<<m_brand<<"牌磁力杯是一个有磁铁的水杯,它能:";
}

AddEffect类
addeffect.h

#include "product.h"

class AddEffect : public Product
{
private:
    Product * m_product;
public:
    AddEffect(Product *product);
    ~AddEffect();

    virtual void effect();
};

addeffect.cpp

#include "addeffect.h"

AddEffect::AddEffect(Product *product)
{
    m_product = product;
}

AddEffect::~AddEffect()
{
}

void AddEffect::effect()
{
    m_product->effect();
}

ProlongLife类
prolonglife.h

#include "addeffect.h"

class ProlongLife : public AddEffect
{
private:
    void addNewEffect();

public:
    ProlongLife(Product * product);
    ~ProlongLife();

    void effect();

};

prolonglife.cpp

#include "prolonglife.h"
#include <iostream>

using namespace std;

void ProlongLife::addNewEffect()
{
    cout<<"延年益寿 ";
}

ProlongLife::ProlongLife(Product *product):AddEffect(product)
{

}

ProlongLife::~ProlongLife()
{

}


void ProlongLife::effect()
{
    AddEffect::effect();
    addNewEffect();
}

TreatingCancer类
treatingcancer.h

#include "addeffect.h"

class TreatingCancer : public AddEffect
{
private:
    void addNewEffect();

public:
    TreatingCancer(Product *product);
    ~TreatingCancer();

    void effect();
};

treatingcancer.cpp

#include "treatingcancer.h"
#include <iostream>

using namespace std;

void TreatingCancer::addNewEffect()
{
    cout<<"防癌治癌 ";
}

TreatingCancer::TreatingCancer(Product *product):AddEffect(product)
{

}

TreatingCancer::~TreatingCancer()
{

}

void TreatingCancer::effect()
{
    AddEffect::effect();
    addNewEffect();
}

TreatingtHypertension类
treatingthypertension.h

#include "addeffect.h"

class TreatingtHypertension : public AddEffect
{
private:
    void addNewEffect();

public:
    TreatingtHypertension(Product *product);
    ~TreatingtHypertension();

    void effect();
};

treatingthypertension.cpp

#include "treatingthypertension.h"
#include <iostream>

using namespace std;

void TreatingtHypertension::addNewEffect()
{
    cout<<"预防和治疗高血压 ";
}

TreatingtHypertension::TreatingtHypertension(Product *product):AddEffect(product)
{

}

TreatingtHypertension::~TreatingtHypertension()
{

}

void TreatingtHypertension::effect()
{
    AddEffect::effect();
    addNewEffect();
}

Factory类
factory.h

#include "product.h"
#include "daliwan.h"
#include "cilibei.h"
#include "addeffect.h"
#include "prolonglife.h"
#include "treatingcancer.h"
#include "treatingthypertension.h"

class Factory
{
public:
    Factory();

    static Product *creatProduct(string product, string brand);
    static AddEffect *printDescription(string effect, Product* product);
};

factory.cpp

#include "factory.h"

Factory::Factory()
{

}

Product *Factory::creatProduct(string product, string brand)
{
    if(product == "DaLiWan")
    {
        return new DaLiWan(brand);
    }
    else if(product == "CiLiBei")
    {
        return new CiLiBei(brand);
    }
    return NULL;
}

AddEffect *Factory::printDescription(string effect, Product* product)
{
    if(effect == "ProlongLife")
    {
        return new ProlongLife(product);
    }
    else if(effect == "TreatingCancer")
    {
       return new TreatingCancer(product);
    }
    else if(effect == "TreatingtHypertension")
    {
       return new TreatingtHypertension(product);
    }
    return NULL;
}

Client类
client.h

#include <iostream>
#include "product.h"
#include "addeffect.h"
#include "factory.h"

using namespace std;

class Client
{
private:
    Product* m_product;
    AddEffect *m_effects[3];
    string m_productType;
    string m_brand;
    string m_orderEffects[3];
    int m_effectsNum;

public:
    Client();
    ~Client();

    bool chooseProduct();
    bool nameProductBrand();
    bool orderEffects();
    void makeProduct();
    void showProduct();

};

string changNumtoEffect(int i);

client.cpp

#include "client.h"
#include <iostream>
using namespace std;

#include "cilibei.h"
#include "daliwan.h"
#include "prolonglife.h"
#include "treatingcancer.h"
#include "treatingthypertension.h"

Client::Client()
{

}

Client::~Client()
{
    for(int i = m_effectsNum -1; i >= 0; i--)
    {
        if(m_effects[i] != NULL) delete m_effects[i];
    }
    if(m_product != NULL) delete m_product;

}

bool Client::chooseProduct()
{
    int num;
    cout<<"Please choose m_product type:\n";
    cout<<"1 大力丸\n"
        <<"2 磁力杯\n"
        <<"Input a number:";
    cin>>num;
    cin.ignore();
    switch (num) {
    case 1:
        m_productType = "DaLiWan";
        break;
    case 2:
        m_productType = "CiLiBei";
        break;
    default:
        return false;
    }

    return true;
}

bool Client::nameProductBrand()
{
    string brand;
    cout<<"Please name product brand:";
    getline(cin, brand);
    m_brand = brand;
    if(m_brand != "")
        return true;
    return false;
}

string changNumtoEffect(int i)
{
    switch (i) {
    case 1:
        return "ProlongLife";
    case 2:
        return "TreatingCancer";
    case 3:
        return "TreatingtHypertension";
    default:
        return "";
    }
}

bool Client::orderEffects()
{
    int num, i = 0;
    string str;
    cout<<"Please order product's effects:\n";
    cout<<"1 延年益寿\n"
        <<"2 防癌治癌\n"
        <<"3 预防和治疗高血压\n";
    cout<<"Please input numbers:";
    int size = sizeof(m_orderEffects)/sizeof(string);
    while(cin>>num && i <= size)
    {
        if(i == size) break;
        str = changNumtoEffect(num);
        if(str != "")
        {
            m_orderEffects[i++] = str;
        }
        if(cin.get() == '\n')
            break;
    }
    m_effectsNum = i;
    if(m_effectsNum == 0) return false;

    return true;
}


void Client::makeProduct()
{
    m_product = Factory::creatProduct(m_productType, m_brand);
    //添加功能说明
    for(int i = 0; i < m_effectsNum; i++)
    {
        if(i == 0)
        {
            m_effects[i] = Factory::printDescription(m_orderEffects[i], m_product);
        }
        else
        {
            m_effects[i] = Factory::printDescription(m_orderEffects[i], m_effects[i-1]);
        }
    }
}

void Client::showProduct()
{
    if( m_effectsNum > 0)
    {
        m_effects[m_effectsNum-1]->effect();
    }
    else
    {
        m_product->effect();
    }
}

main.cpp

#include <iostream>

using namespace std;

#include "client.h"

int main(int argc, char *argv[])
{
   Client client;
   while(!client.chooseProduct())
   {
       cout<<"Error!\n";
   }
   while (!client.nameProductBrand())
   {
       cout<<"Error!\n";
   }
   while(!client.orderEffects())
   {
       cout<<"Error!\n";
   }

   client.makeProduct();
   client.showProduct();

   return 0;
}

运行结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值