go

golang装饰器模式

介绍

装饰器模式(Decorator Pattern)是一种结构型设计模式,通过组合代替继承,它允许动态地向对象添加额外的功能,同时又不改变其接口。装饰器模式通过将对象包裹在一个装饰器类中,从而在运行时扩展对象的行为。

为什么组合优于继承?

  • 灵活性(Flexibility):组合允许对象在运行时动态地改变其行为。对象可以通过组合其他对象来获取所需的功能,并且可以在运行时更改组合的对象,从而实现灵活的行为扩展。相比之下,继承在编译时固定了类的行为,不够灵活。
  • 松耦合(Loose Coupling):组合通过对象之间的接口进行交互,而不是依赖于具体的实现细节。这导致对象之间的耦合度更低,更容易理解、维护和扩展。继承会导致类与其子类之间紧密关联,子类的修改可能会影响到父类和其他子类,增加了耦合度。
  • 代码复用(Code Reusability):组合可以通过将多个对象组合在一起来实现代码的复用。对象之间的功能可以通过组合不同的对象来组合出新的功能,从而避免了重复编写相似的代码。继承也可以实现代码复用,但容易导致继承层次的复杂性,而组合相对更简单明了。
  • 可扩展性(Extensibility):组合允许在不修改现有代码的情况下添加新的功能。通过添加新的组合对象,可以在不影响现有功能的基础上扩展对象的行为。继承在添加新功能时可能需要修改现有的类层次结构,从而影响到其他相关的类。
  • 解耦继承的限制(Avoiding Inheritance Restrictions):继承有一些限制,例如单继承的限制、继承层次的复杂性等。组合没有这些限制,可以更自由地组织对象之间的关系。

涉及的角色有:

  • Component(组件):定义了一个抽象接口,可以是抽象类或接口,描述了被装饰对象和装饰器对象的共同行为。
  • ConcreteComponent(具体组件):实现了组件接口,是被装饰的对象。它是装饰器模式中的初始对象,可以在运行时动态地添加额外的功能。
  • Decorator(装饰器):实现了组件接口,并且内部持有一个组件对象的引用。装饰器具有与组件相同的接口,并且可以包裹(装饰)其他装饰器或具体组件对象。装饰器可以在调用组件的方法之前或之后执行额外的行为,并且可以通过递归组合,以实现多层装饰。
  • ConcreteDecorator(具体装饰器):具体装饰器是装饰器模式中的具体类,通过扩展装饰器类来添加特定的功能或行为。

原理

  1. 创建一个具体组件对象,即被装饰的对象。
  2. 创建一个或多个具体装饰器对象,它们实现了组件接口,并且内部持有一个组件对象的引用。
  3. 通过将具体装饰器对象包裹在具体组件对象上,形成装饰器链。
  4. 当调用具体组件对象的方法时,装饰器链会依次执行额外的行为,并最终调用具体组件对象的方法。

优点

  • 可以在运行时动态地向对象添加额外的功能,而无需修改现有代码。通过使用不同的装饰器组合,可以实现各种功能的灵活组合。
  • 装饰器模式将功能划分为多个具体装饰器类,每个装饰器类只关注特定的功能,使得类的职责更加清晰。

缺点

  • 装饰器模式引入了许多具体装饰器类,可能会增加类的数量,使得代码更加复杂。
  • 如果使用过多的具体装饰器类,可能会导致系统中存在过多的细粒度对象,使得代码难以维护。

使用场景

  • 在不修改现有代码的情况下,动态地给对象添加功能。
  • 需要对对象的功能进行灵活组合和排列组合。
  • 需要为对象添加额外的行为,但是继承关系不适用或不可行。
  • 需要在运行时动态地添加、修改或移除对象的功能。
  • 需要对多个对象分别添加功能,而不影响其他对象。

比如:

  • 日志记录/性能统计:在方法调用前后记录日志信息,例如记录方法的输入参数、返回值以及执行时间等。这样可以在不修改原始方法的情况下,方便地添加日志记录功能。
  • 缓存:通过在方法调用前先检查缓存中是否存在结果,如果存在则直接返回缓存的结果,如果不存在则调用原始方法,并将结果存入缓存中。
  • 输入验证:通过在方法调用前先验证输入的数据是否符合要求,如果不符合则抛出异常或进行其他处理。
  • 权限控制:通过在方法调用前先检查用户的权限,如果用户有权限则继续执行方法,如果没有权限则抛出异常或进行其他处理。
  • 数据加密:通过在方法调用前先对数据进行加密,然后在方法调用后对数据进行解密。
  • 功能切换:通过在方法调用前后切换不同的装饰器,可以实现不同的功能组合。这样可以根据需求灵活地切换对象的功能。

代码示例

日志记录:

package main

import (
    "fmt"
    "log"
    "time"
)

// 定义Logger接口,包含Log方法,用于日志记录
type Logger interface {
    Log(string)
}

// 定义DefaultLogger结构体
type DefaultLogger struct{}

// 实现Logger接口中的Log方法,默认的日志记录方式
func (d DefaultLogger) Log(msg string) {
    log.Println(msg)
}

// 定义DecoratorFunc装饰器函数类型,接受一个string参数,表示需要记录的日志信息
type DecoratorFunc func(string)

// 定义LogDecorator函数,接受一个Logger参数,并返回一个装饰了日志记录功能的新的函数
func LogDecorator(logger Logger) DecoratorFunc {
    // 返回一个匿名函数
    return func(msg string) {
        start := time.Now()
        // 添加日志开始时间戳
        logger.Log(fmt.Sprintf("Start: %s", start))
        // 匿名函数结束后执行
        defer func() {
            // 添加日志结束时间戳
            logger.Log(fmt.Sprintf("End: %s%v", msg, time.Since(start)))
        }()
    }
}

func main() {
    // 创建DefaultLogger实例,传递给LogDecorator函数
    logger := DefaultLogger{}
    // 定义LogDecorator函数返回的装饰器函数
    decoratedLogger := LogDecorator(logger)
    // 调用decoratedLogger函数,传递需要记录的日志信息
    decoratedLogger("use time: ")
}

组合:

package main

import "fmt"

type Jntm interface {
    Sing()
    Dance()
}

type Ikun struct{}

func (i *Ikun) Sing() {
    fmt.Println("唱")
}

func (i *Ikun) Dance() {
    fmt.Println("跳")
}

type Rap struct {
    Jntm
}

func (r *Rap) Sing() {
    //r.Jntm.Sing()
    fmt.Println("rap")
}

type Basketball struct {
    Jntm
}

func (b *Basketball) Sing() {
    //b.Jntm.Sing()
    fmt.Println("篮球")
}

func main() {
    fmt.Println("---ikun---")
    ikun := &Ikun{}
    ikun.Sing()
    ikun.Dance()
    rap := &Rap{
        Jntm: ikun,
    }
    rap.Sing()
    basketball := &Basketball{
        Jntm: ikun,
    }
    basketball.Sing()
}

代码实现中没有Decorator类,主要是因为Go组合的特性。之所以有Decorator,是因为Decorator中有component成员变量,Decorator中函数实现是调用component的函数,所以对于component中的每一个函数,Decorator都需要封装一下,否则无法使用。但是Go组合方式会自动完成这项任务,无需封装,自然也就不需要Decorator了。

装饰器嵌套

package main

import "fmt"

// 定义Photo接口,包含一个GetPhoto方法,用于获取照片信息
type Photo interface {
    GetPhoto() string
}

// 定义BasicPhoto结构体
type BasicPhoto struct{}

// 实现Photo接口的GetPhoto方法,表示基础照片
func (bp *BasicPhoto) GetPhoto() string {
    return "基础图像"
}

// 定义FilterDecorator结构体,包含photo字段
type FilterDecorator struct {
    photo Photo
}

// 实现了Photo接口的GetPhoto方法,表示被装饰的照片
func (fd *FilterDecorator) GetPhoto() string {
    return fd.photo.GetPhoto()
}

// 定义BlackAndWhiteFilter结构体,继承自FilterDecorator结构体,表示黑白滤镜装饰器
type BlackAndWhiteFilter struct {
    FilterDecorator
}

// 实现了Photo接口的GetPhoto方法,给照片添加黑白滤镜
func (bwf *BlackAndWhiteFilter) GetPhoto() string {
    return fmt.Sprintf("给%s加上黑白滤镜", bwf.FilterDecorator.GetPhoto())
}

func main() {
    // 创建一个BasicPhoto实例
    photo := &BasicPhoto{}
    // 并将其传递给BlackAndWhiteFilter结构体的FilterDecorator字段
    filterPhoto := &BlackAndWhiteFilter{
        FilterDecorator: FilterDecorator{
            photo: photo,
        },
    }
    // 调用GetPhoto方法
    result := filterPhoto.GetPhoto()
    fmt.Println(result) // 给基础图像加上黑白滤镜
}
分类: go
0 0 投票数
文章评分
订阅评论
提醒
guest

0 评论
最旧
最新 最多投票
内联反馈
查看所有评论

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部
0
希望看到您的想法,请您发表评论x