为啥装饰器模式适用于指针而不适用于引用?

Posted

技术标签:

【中文标题】为啥装饰器模式适用于指针而不适用于引用?【英文标题】:Why Decorator pattern works with pointers but not references?为什么装饰器模式适用于指针而不适用于引用? 【发布时间】:2020-03-31 05:26:42 【问题描述】:

我刚刚了解了装饰器模式,并尝试编写一个使用该代码的示例。这个例子是关于饮料和一些调味品的。在装饰器中,我有一个饮料的引用变量。提供的饮料是DecafEspresso。可用的调味品是SoyCaramel。例如,如果我用多个Caramel 定义一个Decaf,我得到的结果只是一个带有一个装饰器的Decaf。所以定义Caramel->Caramel->Decaf给我Caramel->Decaf。定义Caramel->Soy->Caramel->Decaf 工作正常。定义Caramel->Soy->Caramel->Caramel->Decaf给我Caramel->Soy->Caramel->Decaf。长话短说,我不能一个接一个地吃两种或多种相同类型的调味品。它们只是一种调味品。如果我使用指针,它工作正常。

代码:

#include <iostream>
//#include "Decaf.h"
//#include "Espresso.h"
//#include "SoyDecorator.h"
//#include "CaramelDecorator.h"

class Beverage

public:

    virtual std::string GetDescription() const = 0;
    virtual int GetCost() const = 0;
;

class CondimentDecorator : public Beverage

public:

    Beverage& beverage;
    CondimentDecorator(Beverage& beverage) : beverage(beverage) 
;

class Espresso : public Beverage

    virtual std::string GetDescription() const override
    
        return "Espresso";
    

    virtual int GetCost() const override
    
        return 5;
    
;

class Decaf : public Beverage

    virtual std::string GetDescription() const override
    
        return "Decaf";
    

    virtual int GetCost() const override
    
        return 4;
    
;

class CaramelDecorator : public CondimentDecorator

public:

    CaramelDecorator(Beverage& beverage) : CondimentDecorator(beverage) 

    virtual std::string GetDescription() const override
    
        return this->beverage.GetDescription() + " with Caramel";
    

    virtual int GetCost() const override
    
        return this->beverage.GetCost() + 2;
    
;

class SoyDecorator : public CondimentDecorator

public:

    SoyDecorator(Beverage& beverage) : CondimentDecorator(beverage) 

    virtual std::string GetDescription() const override
    
        return this->beverage.GetDescription() + " with Soy";
    

    virtual int GetCost() const override
    
        return this->beverage.GetCost() + 1;
    
;

int main()

    Decaf d;
    SoyDecorator s(d);
    CaramelDecorator c(s);
    CaramelDecorator cc(c);

    std::cout << cc.GetDescription() << std::endl;
    std::cout << cc.GetCost() << std::endl;

输出:

Decaf with Soy with Caramel
7

// Expected:
// Decaf with Soy with Caramel with Caramel
// 9

这是相同的代码,但使用指针并且工作正常: https://ideone.com/7fpGSp

【问题讨论】:

如果您调用CaramelDecorator cc(c);,则使用(默认)复制构造函数。您可以删除它以达到您的目的。 (我假设你不需要它。) 【参考方案1】:

通过从指针切换到引用,OP 构造函数签名变得非常类似于(默认)复制构造函数。

    CondimentDecorator(Beverage &beverage) : beverage(beverage) 

对比

    CondimentDecorator(const Beverage&); // generated by compiler

首先,我认为删除复制构造函数就足够了,但编译器仍会尝试使用已删除的构造函数并提出相应的抱怨,因为它不能再使用了。

最后,我能够通过提供响应来解决 OP 的问题。阻止使用复制构造函数的候选者。

(实际上不再需要删除复制构造函数,但我把它留在了。)

class CondimentDecorator : public Beverage

public:

    Beverage& beverage;
    CondimentDecorator(Beverage &beverage) : beverage(beverage) 
    CondimentDecorator(CondimentDecorator &beverage) : beverage(beverage) 
    CondimentDecorator(const CondimentDecorator&) = delete;
;

派生类也必须这样做:

class CaramelDecorator : public CondimentDecorator

public:

    CaramelDecorator(Beverage &beverage) : CondimentDecorator(beverage) 
    CaramelDecorator(CaramelDecorator &beverage) : CondimentDecorator(beverage) 
    //CaramelDecorator(const CaramelDecorator&) = delete;

    virtual std::string GetDescription() const override
    
        return this->beverage.GetDescription() + " with Caramel";
    

    virtual int GetCost() const override
    
        return this->beverage.GetCost() + 2;
    
;

我只为演示修复了CaramelDecorator,但实际上,这必须对class CondimentDecorator 的所有派生类进行。

OP的固定MCVE:

#include <iostream>
//#include "Decaf.h"
//#include "Espresso.h"
//#include "SoyDecorator.h"
//#include "CaramelDecorator.h"

class Beverage

public:
    virtual std::string GetDescription() const = 0;
    virtual int GetCost() const = 0;
;

class CondimentDecorator : public Beverage

public:

    Beverage& beverage;
    CondimentDecorator(Beverage &beverage) : beverage(beverage) 
    CondimentDecorator(CondimentDecorator &beverage) : beverage(beverage) 
    CondimentDecorator(const CondimentDecorator&) = delete;
;

class Espresso : public Beverage

    virtual std::string GetDescription() const override
    
        return "Espresso";
    

    virtual int GetCost() const override
    
        return 5;
    
;

class Decaf : public Beverage

    virtual std::string GetDescription() const override
    
        return "Decaf";
    

    virtual int GetCost() const override
    
        return 4;
    
;

class CaramelDecorator : public CondimentDecorator

public:

    CaramelDecorator(Beverage &beverage) : CondimentDecorator(beverage) 
    CaramelDecorator(CaramelDecorator &beverage) : CondimentDecorator(beverage) 
    //CaramelDecorator(const CaramelDecorator&) = delete;

    virtual std::string GetDescription() const override
    
        return this->beverage.GetDescription() + " with Caramel";
    

    virtual int GetCost() const override
    
        return this->beverage.GetCost() + 2;
    
;

class SoyDecorator : public CondimentDecorator

public:

    SoyDecorator(Beverage &beverage) : CondimentDecorator(beverage) 

    virtual std::string GetDescription() const override
    
        return this->beverage.GetDescription() + " with Soy";
    

    virtual int GetCost() const override
    
        return this->beverage.GetCost() + 1;
    
;

int main()

    Decaf d;
    SoyDecorator s(d);
    CaramelDecorator c(s);
    CaramelDecorator cc(c);

    std::cout << cc.GetDescription() << std::endl;
    std::cout << cc.GetCost() << std::endl;

输出:

Decaf with Soy with Caramel with Caramel
9

Live Demo on coliru


为什么需要额外的候选人?

CondimentDecorator 派生自 Beverage

所以,对于:

CondimentDecorator d;
CondimentDecorator d2(d);

编译器有两种选择来构造d2

    自定义构造函数CondimentDecorator::CondimentDecorator(Beverage &amp;beverage) (默认)复制构造函数CondimentDecorator::CondimentDecorator(const CondimentDecorator&amp;)

首先,必须应用隐式强制转换,但对于复制构造函数,不需要强制转换(或至多,常量强制转换)。

因此,编译器更喜欢复制构造函数(不幸的是,虽然它被删除了)。

因此,必须提供另一个候选者,它需要像复制构造函数这样的隐式强制转换:

    另一个自定义构造函数CondimentDecorator::CondimentDecorator(CondimentDecorator&amp;)

延伸阅读:Overload Resolution

【讨论】:

以上是关于为啥装饰器模式适用于指针而不适用于引用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我们需要装饰器设计模式中的装饰器?

装饰器模式:为啥我们需要抽象装饰器? [复制]

设计模式之-装饰器模式

11 IO流——装饰器设计模式,Filter装饰流

重学设计模式(三设计模式-装饰器模式)

设计模式----装饰器模式