设计模式——7. 装饰器模式

1. 说明

装饰者模式(Decorator Pattern)是一种结构型设计模式,它允许你在不改变对象接口的前提下,动态地将新行为附加到对象上。这种模式是通过创建一个包装(或装饰)对象,将要被装饰的对象包裹起来,从而实现对原有对象功能的增强和扩展。

装饰者模式的主要特点包括:

  1. 不改变接口: 装饰者模式不改变原有对象的接口,允许你向现有对象添加新的行为,而无需修改其代码。
  2. 透明性: 装饰者模式使得装饰器与被装饰的对象可以互换使用,客户端不需要知道具体的装饰器类。
  3. 多层装饰: 可以通过多个装饰器的组合来实现多个不同的行为,这种嵌套装饰的方式可以实现复杂的功能组合。
  4. 开闭原则: 装饰者模式遵循开闭原则,允许在不修改已有代码的情况下扩展功能。
  5. 清晰的单一职责原则: 每个装饰器类负责一个特定的行为扩展,符合单一职责原则。

装饰者模式通常包括以下角色:

  • Component(组件): 定义了一个抽象接口,可以是抽象类或接口,规定了被装饰对象和装饰器的共同接口。
  • ConcreteComponent(具体组件): 实现了组件接口的具体类,是被装饰的对象。
  • Decorator(装饰器): 也是组件接口的子类,它持有一个指向具体组件对象的引用,并可以附加新的行为。
  • ConcreteDecorator(具体装饰器): 实现了装饰器接口的具体类,负责为具体组件对象添加新的行为。

装饰者模式的经典应用包括文本编辑器中的字体样式、咖啡店中的咖啡配料、窗口系统中的窗口装饰等。这些应用中,装饰者模式使得可以动态地添加或删除功能,同时保持代码的灵活性和可扩展性。

2. 使用的场景

装饰者模式在以下情况下特别有用:

  1. 动态扩展功能: 当你需要在不修改现有对象的代码的情况下,动态地向对象添加新功能或行为时,装饰者模式是一种非常有用的设计模式。
  2. 避免类爆炸: 当有多个可能的组合方式时,避免创建大量子类的类层次结构,而使用装饰者模式可以更灵活地组合不同的功能,避免类爆炸问题。
  3. 单一职责原则: 当你希望确保每个类都遵守单一职责原则时,装饰者模式使得你可以将不同的功能分散到不同的装饰器类中,每个装饰器类负责一个特定的职责。
  4. 组合优于继承: 装饰者模式提供了一种更灵活的方式来组合对象的行为,相对于静态的继承,它更加优雅且易于维护。
  5. 在运行时动态添加行为: 当需要在运行时决定对象是否添加某些行为,以及如何添加这些行为时,装饰者模式可以派上用场。
  6. 分层次的配置: 装饰者模式允许你将各种功能按照层次结构进行组织和配置,从而更容易管理复杂的对象。
  7. 可插拔性: 当你希望能够随时插入或删除功能时,装饰者模式允许你以灵活的方式添加或删除装饰器,从而实现可插拔性。
  8. 不影响现有代码: 装饰者模式不需要修改现有对象的代码,因此适用于已经存在的类,无需改变其结构即可扩展功能。

总之,装饰者模式的使用场景涵盖了许多需要动态地增强或扩展对象功能的情况,特别适用于那些需要保持开放-封闭原则和单一职责原则的设计。

3. 应用例子

以下是一个使用Python实现的简单装饰者模式的示例,假设我们有一个咖啡店,需要给咖啡添加不同的调料:

# Component: 咖啡接口
class Coffee:
    def cost(self):
        return 5

# ConcreteComponent: 基础咖啡
class BasicCoffee(Coffee):
    def cost(self):
        return super().cost()

# Decorator: 调料装饰器
class CoffeeDecorator(Coffee):
    def __init__(self, coffee):
        self._coffee = coffee

    def cost(self):
        return self._coffee.cost()

# ConcreteDecoratorA: 奶泡调料
class FoamDecorator(CoffeeDecorator):
    def cost(self):
        return super().cost() + 2

# ConcreteDecoratorB: 巧克力调料
class ChocolateDecorator(CoffeeDecorator):
    def cost(self):
        return super().cost() + 3

# 客户端代码
coffee = BasicCoffee()
print("咖啡价格:", coffee.cost())

coffee_with_foam = FoamDecorator(coffee)
print("加奶泡咖啡价格:", coffee_with_foam.cost())

coffee_with_chocolate = ChocolateDecorator(coffee)
print("加巧克力咖啡价格:", coffee_with_chocolate.cost())

coffee_with_foam_and_chocolate = ChocolateDecorator(FoamDecorator(coffee))
print("加奶泡和巧克力咖啡价格:", coffee_with_foam_and_chocolate.cost())

在这个示例中:

  • Coffee 是咖啡接口,定义了基础咖啡的价格。
  • BasicCoffee 是具体组件,实现了 Coffee 接口,表示基础的咖啡。
  • CoffeeDecorator 是装饰器抽象类,它持有一个指向 Coffee 对象的引用,并实现了 Coffee 接口。
  • FoamDecorator 和 ChocolateDecorator 是具体装饰器类,它们扩展了 CoffeeDecorator 类,分别添加了奶泡和巧克力调料的价格。

在客户端代码中,我们创建了基础咖啡对象,并使用不同的装饰器来装饰它,从而动态地添加奶泡和巧克力调料,并计算总价格。这个示例演示了装饰者模式的灵活性,可以动态地为对象添加功能,而不需要修改其原始类。

4. 实现要素

装饰者模式的实现要素包括以下几个部分:

  1. Component(组件): 定义一个抽象接口或抽象类,用于被具体组件和装饰器共同实现。这个接口或类定义了被装饰的对象的基本行为。
  2. ConcreteComponent(具体组件): 实现了组件接口的具体类,是被装饰的对象,提供了基本的行为。
  3. Decorator(装饰器): 也是组件接口的子类,持有一个指向具体组件对象的引用,以及装饰器独有的行为。它通常是一个抽象类,可以有多个具体装饰器继承自它。
  4. ConcreteDecorator(具体装饰器): 实现了装饰器接口的具体类,负责为具体组件对象添加新的行为。具体装饰器可以通过继承或组合方式来扩展行为。

5. UML图

以下是装饰者模式的UML类图:

+-----------------------+
|        Component     |
+-----------------------+
| + operation()        |
+-----------------------+
          |
          |
          ▼
+-----------------------+
|   ConcreteComponent  |
+-----------------------+
| + operation()        |
+-----------------------+
          |
          |
          ▼
+-----------------------+
|      Decorator       |
+-----------------------+
| - component: Component|
| + operation()        |
+-----------------------+
          |
          |
          ▼
+-----------------------+
|  ConcreteDecoratorA  |
+-----------------------+
| + operation()        |
| + addedBehavior()    |
+-----------------------+
          |
          |
          ▼
+-----------------------+
|  ConcreteDecoratorB  |
+-----------------------+
| + operation()        |
| + addedBehavior()    |
+-----------------------+
  • Component 定义了一个抽象接口或抽象类,其中包含一个 operation() 方法,这是被装饰的对象的基本行为。
  • ConcreteComponent 是具体组件类,实现了 Component 接口,提供了基本的行为。
  • Decorator 是装饰器抽象类,它持有一个指向 Component 对象的引用,并实现了 Component 接口。Decorator 类通常包含一个构造函数,用于接收被装饰的对象。
  • ConcreteDecoratorA 和 ConcreteDecoratorB 是具体装饰器类,它们扩展了 Decorator 类,添加了新的行为。这些具体装饰器可以组合使用以添加多个不同的功能。

客户端代码可以通过创建具体组件对象,并使用具体装饰器来装饰这些对象,从而动态地添加功能。

6. Java/golang/javascrip/C++ 等语言实现方式

6.1 Java实现

上述例子用Java语言实现示例如下:

// Component: 咖啡接口
interface Coffee {
    double cost();
}

// ConcreteComponent: 基础咖啡
class BasicCoffee implements Coffee {
    @Override
    public double cost() {
        return 5.0;
    }
}

// Decorator: 调料装饰器
abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;

    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
}

// ConcreteDecoratorA: 奶泡调料
class FoamDecorator extends CoffeeDecorator {
    public FoamDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double cost() {
        return coffee.cost() + 2.0;
    }
}

// ConcreteDecoratorB: 巧克力调料
class ChocolateDecorator extends CoffeeDecorator {
    public ChocolateDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double cost() {
        return coffee.cost() + 3.0;
    }
}

// 客户端代码
public class CoffeeShop {
    public static void main(String[] args) {
        Coffee coffee = new BasicCoffee();
        System.out.println("咖啡价格: " + coffee.cost());

        Coffee coffeeWithFoam = new FoamDecorator(coffee);
        System.out.println("加奶泡咖啡价格: " + coffeeWithFoam.cost());

        Coffee coffeeWithChocolate = new ChocolateDecorator(coffee);
        System.out.println("加巧克力咖啡价格: " + coffeeWithChocolate.cost());

        Coffee coffeeWithFoamAndChocolate = new ChocolateDecorator(new FoamDecorator(coffee));
        System.out.println("加奶泡和巧克力咖啡价格: " + coffeeWithFoamAndChocolate.cost());
    }
}

6.2 Golang实现

上述例子用golang实现示例如下:

package main

import "fmt"

// Coffee 接口定义了咖啡的基本行为
type Coffee interface {
        Cost() float64
}

// BasicCoffee 实现了 Coffee 接口,表示基础咖啡
type BasicCoffee struct{}

func (c BasicCoffee) Cost() float64 {
        return 5.0
}

// CoffeeDecorator 是装饰器接口
type CoffeeDecorator interface {
        Cost() float64
}

// FoamDecorator 是具体的奶泡调料装饰器
type FoamDecorator struct {
        Coffee Coffee
}

func (f FoamDecorator) Cost() float64 {
        return f.Coffee.Cost() + 2.0
}

// ChocolateDecorator 是具体的巧克力调料装饰器
type ChocolateDecorator struct {
        Coffee Coffee
}

func (c ChocolateDecorator) Cost() float64 {
        return c.Coffee.Cost() + 3.0
}

func main() {
        coffee := BasicCoffee{}
        fmt.Printf("咖啡价格: %.2f\n", coffee.Cost())

        coffeeWithFoam := FoamDecorator{Coffee: coffee}
        fmt.Printf("加奶泡咖啡价格: %.2f\n", coffeeWithFoam.Cost())

        coffeeWithChocolate := ChocolateDecorator{Coffee: coffee}
        fmt.Printf("加巧克力咖啡价格: %.2f\n", coffeeWithChocolate.Cost())

        coffeeWithFoamAndChocolate := ChocolateDecorator{Coffee: FoamDecorator{Coffee: coffee}}
        fmt.Printf("加奶泡和巧克力咖啡价格: %.2f\n", coffeeWithFoamAndChocolate.Cost())
}

6.3 Javascript实现

上述例子用javascript实现示例如下:

// Coffee 接口定义了咖啡的基本行为
class Coffee {
    cost() {
        return 5.0;
    }
}

// FoamDecorator 是具体的奶泡调料装饰器
class FoamDecorator {
    constructor(coffee) {
        this.coffee = coffee;
    }

    cost() {
        return this.coffee.cost() + 2.0;
    }
}

// ChocolateDecorator 是具体的巧克力调料装饰器
class ChocolateDecorator {
    constructor(coffee) {
        this.coffee = coffee;
    }

    cost() {
        return this.coffee.cost() + 3.0;
    }
}

// 客户端代码
const coffee = new Coffee();
console.log("咖啡价格: " + coffee.cost());

const coffeeWithFoam = new FoamDecorator(coffee);
console.log("加奶泡咖啡价格: " + coffeeWithFoam.cost());

const coffeeWithChocolate = new ChocolateDecorator(coffee);
console.log("加巧克力咖啡价格: " + coffeeWithChocolate.cost());

const coffeeWithFoamAndChocolate = new ChocolateDecorator(new FoamDecorator(coffee));
console.log("加奶泡和巧克力咖啡价格: " + coffeeWithFoamAndChocolate.cost());

6.4 C++实现

上述例子用C++实现如下:

#include <iostream>

// Coffee 类定义了咖啡的基本行为
class Coffee {
public:
    virtual double cost() {
        return 5.0;
    }
};

// FoamDecorator 类是具体的奶泡调料装饰器
class FoamDecorator : public Coffee {
private:
    Coffee* coffee;

public:
    FoamDecorator(Coffee* coffee) {
        this->coffee = coffee;
    }

    double cost() override {
        return coffee->cost() + 2.0;
    }
};

// ChocolateDecorator 类是具体的巧克力调料装饰器
class ChocolateDecorator : public Coffee {
private:
    Coffee* coffee;

public:
    ChocolateDecorator(Coffee* coffee) {
        this->coffee = coffee;
    }

    double cost() override {
        return coffee->cost() + 3.0;
    }
};

int main() {
    Coffee* coffee = new Coffee();
    std::cout << "咖啡价格: " << coffee->cost() << std::endl;

    Coffee* coffeeWithFoam = new FoamDecorator(coffee);
    std::cout << "加奶泡咖啡价格: " << coffeeWithFoam->cost() << std::endl;

    Coffee* coffeeWithChocolate = new ChocolateDecorator(coffee);
    std::cout << "加巧克力咖啡价格: " << coffeeWithChocolate->cost() << std::endl;

    Coffee* coffeeWithFoamAndChocolate = new ChocolateDecorator(new FoamDecorator(coffee));
    std::cout << "加奶泡和巧克力咖啡价格: " << coffeeWithFoamAndChocolate->cost() << std::endl;

    delete coffee;
    delete coffeeWithFoam;
    delete coffeeWithChocolate;
    delete coffeeWithFoamAndChocolate;

    return 0;
}

7. 练习题

假设你正在设计一个电子商务平台,该平台销售不同类型的商品。每个商品都有一个基本价格,但用户可以选择添加一些可选的功能(装饰器),这些功能会影响商品的总价格。
使用装饰者模式来实现这个场景。你需要创建一个基本商品类和一些可选的装饰器类,用户可以根据需要组合不同的装饰器来创建具有不同功能的商品。

要求:

  1. 创建一个基本商品类 Product,它包括一个 getPrice() 方法,用于返回商品的基本价格。
  2. 创建两个装饰器类 DiscountDecorator 和 ShippingDecorator,分别用于添加折扣和运费到商品上。这两个装饰器应该继承自一个通用的 Decorator 类,它也实现了 getPrice() 方法。
  3. 用户应该能够创建一个基本商品,并根据需要添加折扣和运费,形成一个包含这些功能的最终商品。
  4. 最终商品的价格应该包括基本价格、折扣和运费的总和。

提示:你可以使用多种方式实现这个场景,例如,可以使用组合、递归等方式来连接装饰器。

你可以在评论区里或者私信我回复您的答案,这样我或者大家都能帮你解答,期待着你的回复~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guohuang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值