模式定义
动态(组合)地给一个对象增加一些额外的职责,就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码并且减少子类个数)
类图
应用场景
扩展一个类的功能或给一个类添加附加职责
优点
1.符合开闭原则
2.不改变原有对象的情况下给一个对象扩展功能
3.使用不同的组合可以实现不同的效果
要点总结
- 通过采用组合而非继承的手法,Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”
- Decorator类在接口上表现为is-a 的继承关系,即Decorator类继承了Component类所具有的接口,但在实现上又表现为has-a的组合关系,即Decorator类又使用了另外一个Component类
- Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个问题方向上的扩展功能”–是为“装饰”的含义
Go语言代码实现
举个例子,假设我们正在开发一个咖啡店的订单系统,我们需要为咖啡提供不同的调料选项,例如牛奶、糖浆、巧克力等。每个调料都有不同的价格,而且可以组合使用。这里,咖啡是原始对象,而调料是装饰者。我们可以通过装饰者模式来动态地为咖啡添加不同的调料。
下面是通过 Golang 实现的简单示例:
package main
import "fmt"
// 咖啡接口
type Coffee interface {
GetDescription() string
GetCost() float64
}
// 原始咖啡
type SimpleCoffee struct{}
func (c *SimpleCoffee) GetDescription() string {
return "简单咖啡"
}
func (c *SimpleCoffee) GetCost() float64 {
return 1.0
}
// 调料装饰者
type CoffeeDecorator struct {
Coffee Coffee
}
func (cd *CoffeeDecorator) GetDescription() string {
return cd.Coffee.GetDescription()
}
func (cd *CoffeeDecorator) GetCost() float64 {
return cd.Coffee.GetCost()
}
// 牛奶调料
type MilkDecorator struct {
CoffeeDecorator
}
func (md *MilkDecorator) GetDescription() string {
return md.CoffeeDecorator.GetDescription() + " + 牛奶"
}
func (md *MilkDecorator) GetCost() float64 {
return md.CoffeeDecorator.GetCost() + 0.5
}
// 糖浆调料
type SyrupDecorator struct {
CoffeeDecorator
}
func (sd *SyrupDecorator) GetDescription() string {
return sd.CoffeeDecorator.GetDescription() + " + 糖浆"
}
func (sd *SyrupDecorator) GetCost() float64 {
return sd.CoffeeDecorator.GetCost() + 0.3
}
func main() {
// 创建原始咖啡
coffee := &SimpleCoffee{}
fmt.Println(coffee.GetDescription()) // 输出:简单咖啡
fmt.Println(coffee.GetCost()) // 输出:1.0
// 添加牛奶调料
coffeeWithMilk := &MilkDecorator{CoffeeDecorator{coffee}}
fmt.Println(coffeeWithMilk.GetDescription()) // 输出:简单咖啡 + 牛奶
fmt.Println(coffeeWithMilk.GetCost()) // 输出:1.5
// 添加糖浆调料
coffeeWithMilkAndSyrup := &SyrupDecorator{CoffeeDecorator{coffeeWithMilk}}
fmt.Println(coffeeWithMilkAndSyrup.GetDescription()) // 输出:简单咖啡 + 牛奶 + 糖浆
fmt.Println(coffeeWithMilkAndSyrup.GetCost()) //
}
通过装饰者模式,我们实现了在简单咖啡的基础上可以添加牛奶和添加糖浆的功能,在简单牛奶咖啡的基础上添加糖浆的任意组合功能。
桥接设计模式和装饰者设计模式有一些相似之处,但也存在一些不同点。
共同点:
结构型模式:桥接模式和装饰者模式都是结构型设计模式,关注对象之间的组织和关联关系。
解耦抽象和实现:两种模式都通过将抽象和实现部分分离,实现了解耦的目的。
动态性:两种模式都具有动态性,可以在运行时动态地添加或修改对象的行为。
不同点:
关注点:桥接模式的主要关注点是将抽象和实现分离,以便它们可以独立地变化。而装饰者模式的主要关注点是在不修改现有对象结构的情况下,动态地添加功能。
目的:桥接模式的目的是将抽象和实现解耦,使它们可以独立变化。而装饰者模式的目的是动态地为对象添加功能,同时保持接口的一致性。
参与者:桥接模式中,抽象部分和实现部分是两个独立的层次结构,它们之间可以自由组合。而装饰者模式中,装饰者对象包装原始对象,并通过组合的方式为其添加功能。
使用方式:桥接模式通常在系统设计初期使用,用于处理两个独立变化的维度。而装饰者模式通常在系统已经存在一些基本功能的情况下使用,用于动态地添加额外的功能。
需要注意的是,桥接模式和装饰者模式虽然具有一些相似之处,但它们的关注点和目的略有不同。在实际应用中,应根据具体需求选择适合的设计模式。