一层层往上加东西,而且东西想加就能加,不相加就不用加,非常容易在原来的基础上扩展。
主要用于在不改变对象原有逻辑的情况下,动态地给对象增加新的行为。
它通过把装饰行为包裹在对象外部来扩展对象的功能。这种方式的好处是,可以在不修改现有代码的情况下,给对象增加新的行为,从而增强其功能。
装饰器模式通常包括一个抽象组件、具体组件、抽象装饰器和具体装饰器。抽象组件是被装饰对象的抽象类或接口,具体组件是被装饰对象的具体实现,抽象装饰器和具体装饰器则是用于扩展组件功能的类。实现思路就是定义一个interface,里面包含要进行的操作
Web过滤应用
package main
import "fmt"
//装饰者模式
//抽象组件
type Compent interface {
Operation()
}
//具体组件
type GetData struct {
}
func (gd *GetData) Operation() {
fmt.Println("---get data---")
}
//抽象装饰器
type FilterArgs struct {
compent Compentaswell
}
func (fa *FilterArgs) Operation() {
fmt.Println("---filter args---")
fa.compent.Operation()
}
type IsLogin struct {
compent Compent
}
func (il *IsLogin) Operation() {
fmt.Println("---is login---")
il.compent.Operation()
}
//装饰者模式,就是一层层往上加东西,而且东西想加就能加,不相加就不用加,非常容易在原来的基础上扩展
//实现思路就是定义一个interface,里面包含要进行的操作
//优点:增加了代码的可维护性和复用性,以在不修改原始缓存系统代码的情况下增强其功能,而不会对原有的代码产生影响
func main() {
//原始数据
gd := GetData{}
//增加参数过滤
filterArgs := FilterArgs{compent: &gd}
//增加是否登录判断
isLogin := IsLogin{compent: &filterArgs}
//执行操作
isLogin.Operation()
}
缓存系统
应用场景是在缓存层和数据存储层之间引入一层装饰者,以增强缓存系统的能力。这样可以在不影响缓存系统和数据存储层之间的交互方式的情况下,增加缓存系统的功能。
下面是一个在缓存系统中应用装饰者模式的示例。我们假设有一个 Redis 缓存系统和一个 MySQL 数据存储层,我们希望在这两个层之间添加一层缓存装饰者来提高性能。
type Cache interface {
Get(key string) (interface{}, error)
Set(key string, value interface{}) error
Delete(key string) error
}
type MemoryCache struct {
cache map[string]interface{}
}
func NewMemoryCache() *MemoryCache {
return &MemoryCache{cache: make(map[string]interface{})}
}
func (mc *MemoryCache) Get(key string) (interface{}, error) {
value, ok := mc.cache[key]
if !ok {
return nil, fmt.Errorf("key '%s' not found", key)
}
return value, nil
}
func (mc *MemoryCache) Set(key string, value interface{}) error {
mc.cache[key] = value
return nil
}
func (mc *MemoryCache) Delete(key string) error {
delete(mc.cache, key)
return nil
}
接下来,我们实现一个装饰器 ExpirationCache,它添加了缓存数据过期的功能。这个装饰器包含了一个内部的 Cache 实例,它可以将操作转发给这个内部实例,同时添加了过期时间的控制。
type ExpirationCache struct {
cache Cache
ttl time.Duration
}
func NewExpirationCache(cache Cache, ttl time.Duration) *ExpirationCache {
return &ExpirationCache{
cache: cache,
ttl: ttl,
}
}
func (ec *ExpirationCache) Get(key string) (interface{}, error) {
value, err := ec.cache.Get(key)
if err != nil {
return nil, err
}
// Check if the value has expired
if value != nil {
_, ok := value.(*expirationValue)
if ok {
if time.Now().After(value.(*expirationValue).expireTime) {
// Value has expired, delete it and return nil
ec.cache.Delete(key)
value = nil
} else {
value = value.(*expirationValue).value
}
}
}
return value, nil
}
func (ec *ExpirationCache) Set(key string, value interface{}) error {
ev := &expirationValue{
value: value,
expireTime: time.Now().Add(ec.ttl),
}
return ec.cache.Set(key, ev)
}
func (ec *ExpirationCache) Delete(key string) error {
return ec.cache.Delete(key)
}
type expirationValue struct {
value interface{}
expireTime time.Time
}
最后,我们可以创建一个具有过期功能的缓存实例
cache := NewExpirationCache(NewMemoryCache(), time.Minute)
// Set a value with a 5 second TTL
cache.Set("key", "value", 5*time.Second)
// Get the value, which will be nil after 5 seconds
value, _ := cache.Get("key")
fmt.Println(value) // "value"
time.Sleep(6 * time.Second)
value, _ = cache.Get("key")
fmt.Println(value) // nil
中间件
package main
import (
"fmt"
"net/http"
"time"
)
type Middleware func(http.HandlerFunc) http.HandlerFunc
func Logging() Middleware {
return func(f http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
defer func() {
fmt.Println(r.URL.Path, time.Since(start))
}()
f(w, r)
}
}
}
func Authentication() Middleware {
return func(f http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Authorization") == "" {
http.Error(w, "Authorization header missing", http.StatusUnauthorized)
return
}
f(w, r)
}
}
}
func Index(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Welcome!")
}
func main() {
http.HandleFunc("/", Logging()(Authentication()(Index)))
http.ListenAndServe(":8080", nil)
}
日志系统
日志系统也是一个常见的装饰器模式的应用。在日志系统中,可以将日志输出器包装在不同的装饰器中,例如输出到控制台、输出到文件、输出到数据库等。这些装饰器可以对日志输出器进行透明的包装,从而实现日志的灵活输出。例如,可以通过配置文件来指定输出方式,同时也可以在运行时动态地添加或删除装饰器,以适应不同的需求。
装饰器模式在日志系统中的应用可以用来实现在不修改原有代码的情况下增加日志记录的功能。具体来说,装饰器模式可以用来包装现有的日志对象,以添加额外的功能。这样,我们可以在不影响原有代码的情况下,动态地将日志功能添加到系统中。
下面给出一个示例,展示如何使用装饰器模式实现一个日志系统。首先,我们定义一个Logger接口,该接口包含了记录日志的基本方法:
type Logger interface {
Log(message string)
}
我们还定义了一个实现了Logger接口的ConsoleLogger结构体,该结构体将日志记录到控制台:
type ConsoleLogger struct{}
func (c *ConsoleLogger) Log(message string) {
fmt.Println("Console Logger: " + message)
}
现在,我们可以使用装饰器模式来增加日志功能。我们定义一个LoggerDecorator接口,该接口也实现了Logger接口,并且包含了额外的方法,用于装饰现有的日志对象:
type LoggerDecorator interface {
Logger
SetLogger(Logger)
}
接下来,我们可以实现一个TimestampDecorator结构体,该结构体会在日志信息前面添加时间戳:
type TimestampDecorator struct {
logger Logger
}
func (t *TimestampDecorator) SetLogger(logger Logger) {
t.logger = logger
}
func (t *TimestampDecorator) (message string) {
t.logger.Log(time.Now().Format("2006-01-02 15:04:05") + " " + message)
}
最后,我们还可以实现一个FileDecorator结构体,该结构体将日志记录到一个文件中:
type FileDecorator struct {
logger Logger
file *os.File
}
func (f *FileDecorator) SetLogger(logger Logger) {
f.logger = logger
}
func (f *FileDecorator) Log(message string) {
f.file.WriteString(message + "\n")
f.logger.Log(message)
}
现在,我们可以使用这些结构体来实现一个具有日志功能的系统。我们可以首先创建一个ConsoleLogger对象,然后用TimestampDecorator结构体装饰它,再用FileDecorator结构体装饰它,以记录日志到文件中:
func main() {
logger := &ConsoleLogger{}
timestampLogger := &TimestampDecorator{}
timestampLogger.SetLogger(logger)
file, _ := os.OpenFile("log.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
fileLogger := &FileDecorator{file: file}
fileLogger.SetLogger(timestampLogger)
fileLogger.Log("hello world")
}
在这个示例中,我们使用了装饰器模式来增加日志功能。我们可以动态地将日志记录到不同的位置,而无需修改原有代码。这使得我们可以轻松地对现有代码进行扩展