装饰模式的定义
装饰模式(Decorator Pattern)是一种结构型设计模式,它允许你动态地为对象添加功能,而无需改变其类。装饰模式通过创建一系列装饰类,这些类实现与被装饰对象相同的接口,并在内部保存一个被装饰对象的引用,从而动态地增加对象的行为。
特点
-
动态扩展对象的功能:相比于通过继承扩展对象功能,装饰模式可以动态地添加或删除功能。
-
灵活组合功能:多个装饰器可以组合使用,以多层次的方式为对象添加功能。
-
遵循开放-封闭原则:装饰模式允许在不修改现有代码的情况下扩展系统功能。
代码示例
1. 基本接口和实现
- ISale 接口:定义了一个基本的收费方法
acceptCash。 - CashNormal 类:实现了
ISale接口,提供了基本的收费算法,即原价收费。
class ISale
{
public:
virtual double acceptCash(double, int) = 0;
virtual ~ISale() {}
};
class CashNormal :public ISale
{
public:
double acceptCash(double price, int num) override
{
return price * num;
}
};
2. 装饰器基类
- CashSuper 类:继承自
ISale接口,并持有一个指向ISale对象的指针m_component。它提供了一个decorate方法,用于设置被装饰的组件,以及一个acceptCash方法,用于调用被装饰对象的方法。
class CashSuper :public ISale
{
public:
void decorate(ISale* component)
{
m_component = component;
}
double acceptCash(double price, int num) override
{
double result = 0;
if (m_component != nullptr)
{
result = m_component->acceptCash(price, num);
}
return result;
}
protected:
ISale* m_component = nullptr;
};
3. 具体装饰器
- CashRebate 类:继承自
CashSuper,实现了打折算法。在acceptCash方法中,先计算打折后的价格,然后调用基类的acceptCash方法。
class CashRebate :public CashSuper
{
public:
CashRebate(double rebate)
{
m_rebate = rebate;
}
double acceptCash(double price, int num) override
{
double result = price * num * m_rebate;
return CashSuper::acceptCash(result, 1);
}
private:
double m_rebate = 1;
};
- CashReturn 类:继承自
CashSuper,实现了满减算法。在acceptCash方法中,先计算满减后的价格,然后调用基类的acceptCash方法。
4. 上下文类
- CashContext 类:根据不同的收费类型,动态组合各种收费算法。
class CashContext
{
public:
CashContext(int type)
{
switch (type)
{
case 1:
cs = new CashNormal();
break;
case 2:
cs = new CashRebate(0.8);
break;
case 3:
cs = new CashRebate(0.7);
break;
case 4:
cs = new CashReturn(300, 100);
break;
case 5:
{
CashNormal* cn = new CashNormal();
CashReturn* cr1 = new CashReturn(300, 100);
CashRebate* cr2 = new CashRebate(0.8);
cr1->decorate(cn); // 用满300返100算法包装原价算法
cr2->decorate(cr1); // 用打8折算法包装满300返100算法
cs = cr2; // 将包装好的算法组合传递给cs对象
break;
}
case 6:
{
CashNormal* cn2 = new CashNormal();
CashRebate* cr3 = new CashRebate(0.7);
CashReturn* cr4 = new CashReturn(200, 50);
cr3->decorate(cn2);
cr4->decorate(cr3);
cs = cr4;
break;
}
}
}
double getResult(double price, int num)
{
return cs->acceptCash(price, num);
}
private:
ISale* cs;
};
5. 客户端代码
在 main 函数中,通过 CashContext 创建具体的收费策略,并计算最终的收费金额。
int main()
{
CashContext* cc = new CashContext(5);
double result = cc->getResult(84.8, 35);
std::cout << "收费金额:" << result << std::endl;
delete cc;
system("pause");
return 0;
}
6.解释
-
ISale 接口: 定义了收费的标准方法
acceptCash,所有收费策略类都要实现这个接口。 -
CashSuper 类: 是一个抽象类,用于装饰具体的收费策略类。它持有一个指向
ISale对象的指针m_component,可以调用被装饰对象的acceptCash方法。 -
具体收费策略类(CashNormal、CashRebate、CashReturn) :
CashNormal实现了正常收费的逻辑。CashRebate实现了打折收费的逻辑,通过调用CashSuper::acceptCash来继续处理被装饰对象的逻辑。CashReturn实现了满减收费的逻辑,通过调用CashSuper::acceptCash来继续处理被装饰对象的逻辑。
-
CashContext 类: 根据传入的类型选择具体的收费策略,并通过组合不同的装饰器实现复杂的收费逻辑。它将最终的收费策略保存在
cs指针中,通过getResult方法返回最终的收费结果。
总结
装饰模式的实现体现在 CashSuper 及其子类的设计中。通过 decorate 方法,可以将一个 ISale 对象传递给另一个 ISale 对象,从而实现动态组合不同的收费策略。
在装饰模式中,调用基类的 acceptCash 方法的目的是为了保持装饰器链的完整性和灵活性。通过调用基类的方法,可以确保装饰器的每一层都能正确地执行其功能,同时将调用传递给下一个装饰器或被装饰对象。这种方式可以递归地应用多个装饰器,使得功能的组合更加灵活和可扩展。