结构型模式-装饰模式

发布时间 2023-11-10 13:41:06作者: Qt小罗

1 什么是装饰模式

装饰模式(Decorator Pattern)是一种结构型设计模式,它允许将新功能动态地添加到对象中,通过将对象放入特殊的包装对象中,这样可以在不改变其接口的情况下,对对象的功能进行逐步扩展。
在装饰模式中,通常包括以下几个角色:抽象构件(Component)、具体构件(Concrete Component)、抽象装饰者(Decorator)和具体装饰者(Concrete Decorator)。抽象构件定义了对象的接口,具体构件是被装饰的对象,抽象装饰者和具体装饰者用于对具体构件进行装饰。

2 举个例子

以下是一个简单的装饰模式示例:假设我们有一个咖啡店,其中有原味咖啡和各种调料(如牛奶、糖、摩卡)。我们可以使用装饰模式为咖啡动态地添加不同的调料而不改变原味咖啡的接口。

首先,我们定义抽象构件 Coffee。它可以是一个抽象类或接口,代表了原味咖啡:

class Coffee {
public:
    virtual std::string getDescription() = 0;
    virtual double cost() = 0;
};

然后,我们创建具体构件 SimpleCoffee,它是原味咖啡:

class SimpleCoffee : public Coffee {
public:
    std::string getDescription() {
        return "Simple Coffee";
    }

    double cost() {
        return 1.0; // 原味咖啡的价格
    }
};

接下来,我们定义抽象装饰者 CoffeeDecorator:

class CoffeeDecorator : public Coffee {
protected:
    Coffee* decoratedCoffee;

public:
    CoffeeDecorator(Coffee* coffee) : decoratedCoffee(coffee) {}

    virtual std::string getDescription() {
        return decoratedCoffee->getDescription();
    }

    virtual double cost() {
        return decoratedCoffee->cost();
    }
};

最后,我们创建具体装饰者,比如 Milk, Sugar 和 Mocha 等:

class Milk : public CoffeeDecorator {
public:
    Milk(Coffee* coffee) : CoffeeDecorator(coffee) {}

    std::string getDescription() {
        return decoratedCoffee->getDescription() + ", Milk";
    }

    double cost() {
        return decoratedCoffee->cost() + 0.5; // 加牛奶的价格
    }
};

class Sugar : public CoffeeDecorator {
public:
    Sugar(Coffee* coffee) : CoffeeDecorator(coffee) {}

    std::string getDescription() {
        return decoratedCoffee->getDescription() + ", Sugar";
    }

    double cost() {
        return decoratedCoffee->cost() + 0.2; // 加糖的价格
    }
};

class Mocha : public CoffeeDecorator {
public:
    Mocha(Coffee* coffee) : CoffeeDecorator(coffee) {}

    std::string getDescription() {
        return decoratedCoffee->getDescription() + ", Mocha";
    }

    double cost() {
        return decoratedCoffee->cost() + 0.3; // 加摩卡的价格
    }
};

客户端代码可以这样使用装饰模式:

int main() {
    Coffee* simpleCoffee = new SimpleCoffee();
    std::cout << "Description: " << simpleCoffee->getDescription() << ", Cost: " << simpleCoffee->cost() << std::endl;

    Coffee* milkCoffee = new Milk(simpleCoffee);
    std::cout << "Description: " << milkCoffee->getDescription() << ", Cost: " << milkCoffee->cost() << std::endl;

    Coffee* mochaSugarCoffee = new Mocha(new Sugar(simpleCoffee));
    std::cout << "Description: " << mochaSugarCoffee->getDescription() << ", Cost: " << mochaSugarCoffee->cost() << std::endl;

    delete simpleCoffee;
    delete milkCoffee;
    delete mochaSugarCoffee;

    return 0;
}

输出如下内容:

Description: Simple Coffee, Cost: 1
Description: Simple Coffee, Milk, Cost: 1.5
Description: Simple Coffee, Milk, Sugar, Mocha, Cost: 2

在第一个输出中,我们使用原味咖啡(SimpleCoffee),其描述为 “Simple Coffee”,价格为 1。
在第二个输出中,我们使用了装饰者 Milk 对原味咖啡进行装饰,因此描述变为 “Simple Coffee, Milk”,价格变为 1 + 0.5 = 1.5。
在第三个输出中,我们首先使用装饰者 Sugar 对原味咖啡进行装饰,得到 “Simple Coffee, Sugar”,然后再使用装饰者 Mocha 对上一次装饰的结果进行装饰,得到最终的描述 “Simple Coffee, Milk, Sugar, Mocha”,价格为 1 + 0.5 + 0.2 + 0.3 = 2。
在上面的例子中,我们使用装饰模式为原味咖啡动态地添加牛奶、糖、摩卡等调料,而不需要改变原味咖啡的接口。这样,我们可以灵活地组合不同的调料,以获得不同口味的咖啡。

3 总结

通过装饰模式,我们实现了对对象功能的动态扩展,同时保持了对象接口的一致性。