GO设计模式——8、桥接模式(结构型)

目录

[桥接模式(Bridge Pattern)](#桥接模式(Bridge Pattern))

桥接模式的核心角色

优缺点

使用场景

代码实现


桥接模式(Bridge Pattern)

桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类,这两种类型的类可被结构化改变而互不影响。桥接模式的目的是将抽象与实现分离,使它们可以独立地变化,该模式通过将一个对象的抽象部分与它的实现部分分离,使它们可以独立地改变。它通过组合的方式,而不是继承的方式,将抽象和实现的部分连接起来。

桥接模式的 核心角色

  • 抽象(Abstraction):用于定义抽象类的接口,它一般是抽象类而不是接口,其中定义了一个 Implementor(实现类接口)类型的对象并可以维护该对象,它与 Implementor 之间具有关联关系,它既可以包含抽象业务方法,也可以包含具体业务方法。
  • 扩展抽象(Refined Abstraction):扩充由 Abstraction 定义的接口,通常情况下它不再是抽象类而是具体类,它实现了在 Abstraction 中声明的抽象业务方法,在 RefinedAbstraction 中可以调用在 Implementor 中定义的业务方法,可以是抽象类的子类或具体实现类。
  • 实现(Implementor):定义实现类的接口,这个接口不一定要与 Abstraction 的接口完全一致,事实上这两个接口可以完全不同,一般而言,Implementor 接口仅提供基本操作,而 Abstraction 定义的接口可能会做更多更复杂的操作。Implementor 接口对这些基本操作进行了声明,而具体实现交给其子类。通过关联关系,在 Abstraction 中不仅拥有自己的方法,还可以调用到 Implementor 中定义的方法,使用关联关系来替代继承关系。
  • 具体实现(Concrete Implementor):具体实现 Implementor 接口,在不同的 ConcreteImplementor 中提供基本操作的不同实现,在程序运行时,ConcreteImplementor 对象将替换其父类对象,提供给抽象类具体的业务操作方法。

优缺点

(1)优点:

  • 抽象和实现的分离。
  • 优秀的扩展能力。
  • 实现细节对客户透明。

(2)缺点:桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。

使用场景

  • 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
  • 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
  • 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。

代码实现

Go 复制代码
package main

import "fmt"

// 消息发送接口
type MessageSend interface {
    send(msg string)
}

// 短信消息
type SMS struct {
}

func (s *SMS) send(msg string) {
    fmt.Println("sms 发送的消息内容为: " + msg)
}

// 邮件消息
type Email struct {
}

func (e *Email) send(msg string) {
    fmt.Println("email 发送的消息内容为: " + msg)
}

// AppPush消息
type AppPush struct {
}

func (a *AppPush) send(msg string) {
    fmt.Println("appPush 发送的消息内容为: " + msg)
}

// 站内信消息
type Letter struct {
}

func (l *Letter) send(msg string) {
    fmt.Println("站内信 发送的消息内容为: " + msg)
}

// 用户触达父类,包含触达方式数组messageSends
type Touch struct {
    messageSends []MessageSend
}

// 触达方法,调用每一种方式进行触达
func (t *Touch) do(msg string) {
    for _, s := range t.messageSends {
       s.send(msg)
    }
}

// 紧急消息做用户触达
type TouchUrgent struct {
    base Touch
}

// 紧急消息,先从db中获取各种信息,然后使用各种触达方式通知用户
func (t *TouchUrgent) do(msg string) {
    fmt.Println("touch urgent 从db获取接收人等信息")
    t.base.do(msg)
}

// 普通消息做用户触达
type TouchNormal struct {
    base Touch
}

// 普通消息,先从文件中获取各种信息,然后使用各种触达方式通知用户
func (t *TouchNormal) do(msg string) {
    fmt.Println("touch normal 从文件获取接收人等信息")
    t.base.do(msg)
}

func main() {
    //触达方式
    sms := &SMS{}
    appPush := &AppPush{}
    letter := &Letter{}
    email := &Email{}
    //根据触达类型选择触达方式
    fmt.Println("-------------------touch urgent")
    touchUrgent := TouchUrgent{
       base: Touch{
          messageSends: []MessageSend{sms, appPush, letter, email},
       },
    }
    touchUrgent.do("urgent情况")
    fmt.Println("-------------------touch normal")
    touchNormal := TouchNormal{ //
       base: Touch{
          messageSends: []MessageSend{sms, appPush, letter, email},
       },
    }
    touchNormal.do("normal情况")
}
相关推荐
我不会起名字3222 小时前
Redis 从 0 到 1(十七):订单 30 分钟不支付自动关闭——ZSet 延迟队列 Go 实现
redis·go·延迟队列·zset·订单
利威尔xu3 小时前
Go IM 源码实战(14):本地缓存快 3000 倍,失效却难 10 倍
后端·缓存·微服务·面试·golang
利威尔xu4 小时前
Go IM 源码实战(13):30 个会话只有 2 个红点,未读数错在三条路径
redis·后端·微服务·面试·golang
FungLeo5 小时前
成为全栈·Go 后端篇·从 Node SQLite 到 PostgreSQL:把迁移做成独立交付物
postgresql·golang·sqlite·go·数据库迁移·成为全栈
高晶6 小时前
SPMI 控制器 RTL 设计与验证新手入门
设计模式·程序员·架构
皓月盈江10 小时前
GO使用Gin框架实现模板定义、解析与渲染
linux·golang·go·gin·定义模板·解析模板·渲染模板
利威尔xu10 小时前
万字详解|Go 进阶(五):sync 包底层:Mutex 自旋 4 次转饥饿,WaitGroup 漏 1 个 Done 挂起 3000 goroutine
后端·golang·并发编程·源码分析·sync
我不会起名字32211 小时前
Redis 从 0 到 1(十九):JWT 还是 Redis Session?登录态存储的 Go 实现
redis·go·jwt·session·登录态
hourinX11 小时前
一张只有两种颜色的头像,为什么还要用 NRGBA?—— `gdenticon` 重构日志(四)
go
我不会起名字32211 小时前
Redis 从 0 到 1(十六):1 亿 UV 只占 12KB?计数去重三件套的 Go 实测
redis·go·uv·布隆过滤器·hyperloglog