【设计模式】桥接,是设计模式?对,其实你用过

桥接模式是结构型设计模式

GoF定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。

优点:用组合/聚合的方式来共享一些能用的方法 。

这个模式非常像网卡的桥接模式,把两个不同的局域网通过网卡连接起来。

简单示例

先看一个简单的示例:

go 复制代码
package main  
  
import"fmt" 

type Implementor interface {  
    OperationImp()  
}  
  
type ConcreteImplementorA struct{}  
  
func NewConcreteImplementorA() *ConcreteImplementorA {  
    return &ConcreteImplementorA{}  
}  
  
func (c *ConcreteImplementorA) OperationImp() {  
    fmt.Println("具体实现A")  
}  
  
type ConcreteImplementorB struct{}  
  
func NewConcreteImplementorB() *ConcreteImplementorB {  
    return &ConcreteImplementorB{}  
}  
  
func (c *ConcreteImplementorB) OperationImp() {  
    fmt.Println("具体实现A")  
}  
  
type RefinedAbstraction struct {  
    Imp Implementor  
}  
  
func NewRefinedAbstraction() *RefinedAbstraction {  
    return &RefinedAbstraction{}  
}  
  
func (r *RefinedAbstraction) Operation() {  
    r.Imp.OperationImp()  
}  
func (r *RefinedAbstraction) SetImplementor(imp Implementor) {  
    r.Imp = imp  
}

func main() {  
    implementorA := NewConcreteImplementorA()  
    implementorB := NewConcreteImplementorB()  
  
    refinedAbstraction := NewRefinedAbstraction()  
  
    refinedAbstraction.SetImplementor(implementorA)  
    refinedAbstraction.Operation()  
  
    refinedAbstraction.SetImplementor(implementorB)  
    refinedAbstraction.Operation()  
}

上述这段代码,可以很容易理解。
SetImplementor(implementorA) 和 SetImplementor(implementorB)

可以动态的切换 Operation() 的实现,也就是把 Operation() 进行了抽象 。

实际案例

场景

会员系统,需要在用户订阅到货通知后,引导用户关注公众号,具体要求如下:

1.PC端:订阅了商品到货通知后,判断用户是否关注了公众号,没关注,需要展示公众号的二维码;已关注,不用展示。

2.微信小程序客户端:订阅了商品到货通知后,判断用户是否关注了公众号,没关注,需打开微信公众号文章,文章中会展示出 公众号的二维码,用户可以长按进行关注;已关注,不用展示。

3.手机浏览器H5客户端:订阅了商品到货通知后,不需要判断是否关注公众号。

对于不同端进行不同的操作,可以把这部分进行抽象。通过调用方法进行切换。

定义接口

go 复制代码
type ISubscribe interface {  
    Subscribe(agent, productName string)  
}

按照不同的客户端进行 Subscribe 方法的定制

PC端

go 复制代码
type PCAlertStockSubscribe struct{}  
  
func NewPCAlertStockSubscribe() ISubscribe {  
    return &PCAlertStockSubscribe{}  
}  
  
func (p *PCAlertStockSubscribe) Subscribe(agent, productName string) {  
    fmt.Println("记录到货知订阅")  
    fmt.Printf("客户端: %v, 商品: %v, 到货通知订阅成功\n", agent, productName)  
  
    fmt.Println("判断是否需引导用户关注公众号")  
    fmt.Println("获取微信公众号的二维码")  
    fmt.Println("引导用户关注公众号,展示微信公众号二维码")  
}

微信小程序端

go 复制代码
  
type MiniProgramAlertStockSubscribe struct{}  
  
func NewMiniProgramAlertStockSubscribe() ISubscribe {  
    return &MiniProgramAlertStockSubscribe{}  
}  
  
func (m *MiniProgramAlertStockSubscribe) Subscribe(agent, productName string) {  
    fmt.Println("记录到货知订阅")  
    fmt.Printf("客户端: %v, 商品: %v, 到货通知订阅成功\n", agent, productName)  
  
    fmt.Println("判断是否需引导用户关注公众号")  
    fmt.Println("获取微信公众号的文章的连接")  
    fmt.Println("引导用户关注公众号,打开微信公众号文章,可以长按二维码进行关注")  
}

手机浏览器H5端

go 复制代码
type MobileAlertStockSubscribe struct{}  
  
func NewMobileAlertStockSubscribe() ISubscribe {  
    return &MobileAlertStockSubscribe{}  
}  
  
func (m *MobileAlertStockSubscribe) Subscribe(agent, productName string) {  
    fmt.Println("记录到货知订阅")  
    fmt.Printf("客户端: %v, 商品: %v, 到货通知订阅成功\n", agent, productName)  
}

通过 ProductAlertStockSubscribe 进行切换

go 复制代码
type IProductSubscribe interface {  
    SetImpSubscribe(imp ISubscribe)  
    ExecSubscribe(agent, productName string)  
}  
  
type ProductAlertStockSubscribe struct {  
    Imp ISubscribe  
}  
  
func NewProductAlertStockSubscribe() IProductSubscribe {  
    return &ProductAlertStockSubscribe{}  
}  
  
func (p *ProductAlertStockSubscribe) SetImpSubscribe(imp ISubscribe) {  
    p.Imp = imp  
}  
  
func (p *ProductAlertStockSubscribe) ExecSubscribe(agent, productName string) {  
    p.Imp.Subscribe(agent, productName)  
}

ProductAlertStockSubscribe 就可以类比网卡,调用 SetImpSubscribe 方法 ,让不同端的逻辑进行切换。

还需要定义一个映射关系,用于切换的客户端,这部分其实是策略模式。

go 复制代码
type clientSubscribeFunc func() ISubscribe  
  
const (  
    WebAgent          = "web"  
    WapAgent          = "wap"  
    MiniProgramAgent  = "miniProgram"  
)  
  
var clientAlertStockSubscribeMap = map[string]clientSubscribeFunc{  
    WebAgent:         NewPCAlertStockSubscribe,  
    MiniProgramAgent: NewMiniProgramAlertStockSubscribe,  
    WapAgent:         NewMobileAlertStockSubscribe,  
}

按照不同的客户端进行调用

go 复制代码
func main() {  
    productName := "【设计模式:可复用面向对象软件的基础】"  
  
    productAlertStockSubscribe := NewProductAlertStockSubscribe()  
    agents := []string{WebAgent, MiniProgramAgent, WapAgent}  
    for _, agent := range agents {  
       // 策略模式的体现  
       alertStockSubscribeFunc := clientAlertStockSubscribeMap[agent]  
       alertStockSubscribe := alertStockSubscribeFunc()  
  
       // 桥接模式  
       productAlertStockSubscribe.SetImpSubscribe(alertStockSubscribe)  
       productAlertStockSubscribe.ExecSubscribe(agent, productName)  
       fmt.Println()  
    }  
}

完整代码如下:

go 复制代码
package main

import "fmt"

type ISubscribe interface {  
    Subscribe(agent, productName string)  
}  
  
type PCAlertStockSubscribe struct{}  
  
func NewPCAlertStockSubscribe() ISubscribe {  
    return &PCAlertStockSubscribe{}  
}  
  
func (p *PCAlertStockSubscribe) Subscribe(agent, productName string) {  
    fmt.Println("记录到货知订阅")  
    fmt.Printf("客户端: %v, 商品: %v, 到货通知订阅成功\n", agent, productName)  
  
    fmt.Println("判断是否需引导用户关注公众号")  
    fmt.Println("获取微信公众号的二维码")  
    fmt.Println("引导用户关注公众号,展示微信公众号二维码")  
}  
  
type MiniProgramAlertStockSubscribe struct{}  
  
func NewMiniProgramAlertStockSubscribe() ISubscribe {  
    return &MiniProgramAlertStockSubscribe{}  
}  
  
func (m *MiniProgramAlertStockSubscribe) Subscribe(agent, productName string) {  
    fmt.Println("记录到货知订阅")  
    fmt.Printf("客户端: %v, 商品: %v, 到货通知订阅成功\n", agent, productName)  
  
    fmt.Println("判断是否需引导用户关注公众号")  
    fmt.Println("获取微信公众号的文章的连接")  
    fmt.Println("引导用户关注公众号,打开微信公众号文章,可以长按二维码进行关注")  
}  
  
type MobileAlertStockSubscribe struct{}  
  
func NewMobileAlertStockSubscribe() ISubscribe {  
    return &MobileAlertStockSubscribe{}  
}  
  
func (m *MobileAlertStockSubscribe) Subscribe(agent, productName string) {  
    fmt.Println("记录到货知订阅")  
    fmt.Printf("客户端: %v, 商品: %v, 到货通知订阅成功\n", agent, productName)  
}  
  
type IProductSubscribe interface {  
    SetImpSubscribe(imp ISubscribe)  
    ExecSubscribe(agent, productName string)  
}  
  
type ProductAlertStockSubscribe struct {  
    Imp ISubscribe  
}  
  
func NewProductAlertStockSubscribe() IProductSubscribe {  
    return &ProductAlertStockSubscribe{}  
}  
  
func (p *ProductAlertStockSubscribe) SetImpSubscribe(imp ISubscribe) {  
    p.Imp = imp  
}  
  
func (p *ProductAlertStockSubscribe) ExecSubscribe(agent, productName string) {  
    p.Imp.Subscribe(agent, productName)  
}  
  
type clientSubscribeFunc func() ISubscribe  
  
const (  
    WebAgent         = "web"  
    WapAgent         = "wap"  
    MiniProgramAgent = "miniProgram"  
)  
  
var clientAlertStockSubscribeMap = map[string]clientSubscribeFunc{  
    WebAgent:         NewPCAlertStockSubscribe,  
    MiniProgramAgent: NewMiniProgramAlertStockSubscribe,  
    WapAgent:         NewMobileAlertStockSubscribe,  
}

func main() {  
    productName := "【设计模式:可复用面向对象软件的基础】"  
  
    productAlertStockSubscribe := NewProductAlertStockSubscribe()  
    agents := []string{WebAgent, MiniProgramAgent, WapAgent}  
    for _, agent := range agents {  
       // 策略模式的体现  
       alertStockSubscribeFunc := clientAlertStockSubscribeMap[agent]  
       alertStockSubscribe := alertStockSubscribeFunc()  
  
       // 桥接模式  
       productAlertStockSubscribe.SetImpSubscribe(alertStockSubscribe)  
       productAlertStockSubscribe.ExecSubscribe(agent, productName)  
       fmt.Println()  
    }  
}

总结

怎么样?是不是觉得很熟悉,是不是感觉已经使用过了,只是不知道这个是桥接模式。

体现了对扩展开放、修改关闭 设计原则

后续再次遇到这样类似的,我想你应该知道该怎么做了。

相关推荐
余槐i3 小时前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
需要8263 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
凤山老林4 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
伯恩bourne5 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
张彦峰ZYF5 小时前
停顿短不等于延迟低:G1、ZGC 与低延迟服务的目标分野、代价账本与选型标定
后端·回收暂停、应用停止总时长·到达安全点、分配停滞·屏障吃吞吐、并发线程吃核心·并发窗口吃堆余量·分区式靠挑回收集兑现目标·染色指针式不支持压缩引用
pride.li6 小时前
第03章 Python 教程-基础语法:注释与变量
服务器·前端·后端·python·算法·计算机视觉
upala6 小时前
【仓颉语言入门 · 第二篇 第 6 课】
后端
罗丹丹Luna6 小时前
移动端显示“已提交”以后:SAP 与低代码集成该怎样暴露真实状态
后端·架构
码事漫谈6 小时前
因为太想讨好你,所以AI会骗你
后端
吴凡wudianmei6 小时前
每调一次模型都花钱?如何用工程手段把调用压到最少?
前端·后端·架构