【建议收藏】如何用Go写出优美的代码-Go的设计模式【适配器模式,桥模式,组合模式】篇四

2022-12-01 16:04:32 浏览数 (1)

大家好,我是追麾(hui)。

这是Go的设计模式第四篇,这篇主要分享适配器模式,桥模式,组合模式。下面我们来看具体模式。

Go的适配器模式

业界适配器模式定义:适配器(Adapter)指将某种接口或数据结构转换为客户端期望的类型,使得不兼容的类或对象能够一起协作。

适配器模式优缺点

  • 优点
    • 将目标类和适配者类解耦:解决了目标类和适配者类接口不一致的问题。这样通过适配器可以透明地调用目标接口,在很多业务场景中符合开闭原则。
    • 复用现存的类:解决了目标类和适配者类的不一致问题。
  • 缺点
    • 增加了系统的复杂性:适配器编写过程需要结合业务场景全面考虑。
    • 增加代码的阅读复杂度:降低代码可读性,过多适配器会让系统越来越复杂。

适配器模式的应用场景

  • 想要使用已存在的目标类(或对象),但它没有提供客户端所需要的接口类型,而更改目标类(或对象)或客户端已有代码的代价都很大。
  • 想要复用某个类,但使用该类的客户类信息是预先无法知道的。

Go适配器模式实现方式

适配器模式(Adapter)包含以下主要角色。

  • 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
  • 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
  • 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

适配器模式在我所接触的业务中,一个是支付SDK的集成形成同一个支付接口调用,聚合广告SDK的集成形成统一广告接口调用。下面我们来具体看示例应用。通过适配器实现支付宝SDK和微信SDK的集成。

代码语言:javascript复制
package main

import "fmt"

func main() {
    // 初始化对接接口
 var t TargetInterface
 // 同时调用支付宝和微信支付
 t = &NewAdapter{
  AlipayInterface:    &AlipayPay{},
  WeixinPayInterface: &WeixinPay{},
 }
 // 这里业务中基于一个用户同时只能调用一种支付方式。
 t.DealDiffPay("weixinpay", 99)
 t.DealDiffPay("alipay", 100)
}

// 支付宝支付SDK
type AlipayInterface interface {
 Pay(money int)
}

type AlipayPay struct {
}

func (a *AlipayPay) Pay(money int) {
 fmt.Println("这里是支付宝支付:", "费用是:", money)
}

// 微信支付SDK
type WeixinPayInterface interface {
 WXPay(money int)
}

type WeixinPay struct {
}

func (a *WeixinPay) WXPay(money int) {
 fmt.Println("这里是微信支付:", "费用是:", money)
}

// 目标接口,能支持传入支付宝或者微信支付进行支付
type TargetInterface interface {
 DealDiffPay(payType string, money int)
}

//自己的adapter,实现微信和支付宝支付,
type NewAdapter struct {
 AlipayInterface
 WeixinPayInterface
}

func (n *NewAdapter) DealDiffPay(payType string, money int) {
 if payType == "alipay" {
  n.AlipayInterface.Pay(money)
 } else if payType == "weixinpay" {
  n.WeixinPayInterface.WXPay(money)
 }
}

Go的桥接模式

业界桥接模式定义:桥(Bridge)使用组合关系将代码的实现层和抽象层分离,让实现层与抽象层代码可以分别自由变化。

桥接模式优缺点

  • 优点
    • 实现抽象和实现的分离,扩展能力强
    • 提高了系统的可扩充性:在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
  • 缺点
    • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
    • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

桥接模式的应用场景

  • 抽象层代码和实现层代码分别需要自由扩展。
  • 需要独立封装或复用实现层代码。

Go桥接模式实现方式

关于上面我们看了桥接模式的定义,自己分离抽象和实现,这个抽象是指对象的抽象,这类对象的总称。实现这个就比较简单,是指具体干啥,是什么。我们具体两个例子,我们现在业界直播非常火,我们要送礼给主播,把送礼当成是一个对象抽象,比如送带水晶黄色皇冠,蓝色飞机啥的是具体的实现。

桥接模式的结构:

  • Abstraction(抽象类,比如例子中送礼):用于定义抽象类接口
  • RefinedAbstratction(扩展抽象类:比如例子中送皇冠,飞机):扩充由Abstraction定义的接口,通常情况下它不再是抽象类而是具体类,实现了在Abstraction中声明的抽象业务方法,在RefinedAbstraction中可以调用在Implementor中定义的业务方法。
  • Implementor(实现类接口,比如例子中颜色,装饰等):定义实现类的接口,一般而言,它不与Abstraction的接口一致。它只提供基本的或者简单的操作。
  • ConcreteImplementor(具体实现类,比如水晶,黄色,蓝色):具体实现Implementor接口,在不同的ConcreteImplementor中提供基本操作的不同实现,在程序运行时,ConcreteImplentor将替换其父类对象,提供给抽象类具体的业务操作方法。

好了,下面我们来具体讲解一下例子。

代码语言:javascript复制
package main

import (
 "fmt"
)

func main() {
 // 送一个蓝色的带水晶的皇冠
 color := Blue{}
 crystal := Crystal{}
 c := Crown{
  Color:   &color,
  DressUp: &crystal,
 }
 c.SendGift()

 // 送一个红色的飞机
 color2 := Red{}
 p := Plane{
  Color:   &color2,
  DressUp: nil,
 }
 p.SendGift()
}

// 颜色
type Color interface {
 Use()
}

// 红色
type Red struct {
}

func (r *Red) Use() {
 fmt.Println("use red color")
}

// 蓝色
type Blue struct {
}

func (b *Blue) Use() {
 fmt.Println("use blue color")
}

// 装扮
type DressUp interface {
 Decorate()
}

// 水晶
type Crystal struct {
}

func (c *Crystal) Decorate() {
 fmt.Println("use crystal dress up")
}

// 抽象类,
type Abstraction interface {
 SendGift()
}

// 飞机
type Plane struct {
 Color
 DressUp
}

func (p *Plane) SendGift() {
 p.Use()
 if p.DressUp != nil {
  p.DressUp.Decorate()
 }
 fmt.Println("送飞机礼物")
}

// 皇冠
type Crown struct {
 Color
 DressUp
}

func (c *Crown) SendGift() {
 c.Use()
 if c.DressUp != nil {
  c.DressUp.Decorate()
 }
 fmt.Println("送皇冠礼物")
}

Go的组合模式

业界组合模式模式定义:组合(Composite)是指使用组合和继承关系将聚合体及其组成元素分解成树状结构,以便客户端在不需要区分聚合体或组成元素类型的情况下使用统一的接口操作它们。

组合模式模式优缺点

  • 优点
    • 高层模块调用简单
    • 更容易在组合体内加入新的对象:客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”
  • 缺点
    • 设计较复杂:客户端需要花更多时间理清类之间的层次关系;

组合模式模式的应用场景

  • 在需要表示一个对象整体与部分的层次结构的场合。
  • 为了简化代码结构,客户端要以统一的方式操作聚合体及其组成元素。

Go组合模式模式实现方式

组合模式包含以下主要角色。

  • 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)。
  • 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
  • 树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。

我们就拿现今社会的等级划分来讲述实现组合模式实现。下面我们来具体看代码实现。

代码语言:javascript复制
package main

import (
 "container/list"
 "fmt"
 "reflect"
 "strconv"
)

func main() {
 // 创建现今社会的等级
 mainLevel := NewPeopleLevel("高层领导干部", "社会上层", 1000000000)

 // 中上层
 level11 := NewPeopleLevel("中层领导干部", "中上层", 10000000)
 level12 := NewPeopleLevel("大企业中层管理人员", "中上层", 10000000)

 // 中中层
 level1111 := NewPeopleLevel("小企业主", "中中层", 10000)
 level1112 := NewPeopleLevel("办事人员", "中中层", 10000)

 // 中下层
 level111111 := NewPeopleLevel("农民工程序员", "中下层", 1000)
 level111112 := NewPeopleLevel("个体服务者", "中下层", 1000)

 // 底层
 level1111111 := NewPeopleLevel("失业人员", "底层", 100)

 //组成当前人类等级
 // 上层添加中上层
 mainLevel.Add(level11)
 mainLevel.Add(level12)

 // 中上层添加中中层
 level11.Add(level1111)
 level12.Add(level1112)

 // 中中层添加中下层
 level1111.Add(level111111)
 level1112.Add(level111112)

 // 中下层添加底层
 level111111.Add(level1111111)

 // 打印今社会的等级
 fmt.Println(mainLevel.ToString())
 for i := mainLevel.SubList.Front(); i != nil; i = i.Next() {
  em := i.Value.(*PeopleLevel)
  fmt.Println(em.ToString())
  for j := i.Value.(*PeopleLevel).SubList.Front(); j != nil; j = j.Next() {
   em := j.Value.(*PeopleLevel)
   fmt.Println(em.ToString())
   for k := j.Value.(*PeopleLevel).SubList.Front(); k != nil; k = k.Next() {
    em := k.Value.(*PeopleLevel)
    fmt.Println(em.ToString())
    for l := k.Value.(*PeopleLevel).SubList.Front(); l != nil; l = l.Next() {
     em := l.Value.(*PeopleLevel)
     fmt.Println(em.ToString())
    }
   }
  }
 }

}

// 人等级 对象
type PeopleLevel struct {
 Name        string
 Role        string
 IncomeLevel int
 SubList     *list.List
}

// 添加子等级
func (p *PeopleLevel) Add(o *PeopleLevel) {
 p.SubList.PushBack(o)
}

// 删除一个等级
func (p *PeopleLevel) Remove(o *PeopleLevel) {
 for i := p.SubList.Front(); i != nil; i = i.Next() {
  if reflect.DeepEqual(i.Value, o) {
   p.SubList.Remove(i)
  }
 }
}

// 获取等级列表
func (p *PeopleLevel) GetSubList() *list.List {
 return p.SubList
}

//  获取等级的string信息
func (p *PeopleLevel) ToString() string {
 return "[ Name: "   p.Name   ", Role: "   p.Role   ", IncomeLevel: "   strconv.Itoa(p.IncomeLevel)   " ]"
}

// 实例化 人类等级对象
func NewPeopleLevel(name, role string, income int) *PeopleLevel {
 sub := list.New()
 return &PeopleLevel{
  Name:        name,
  Role:        role,
  IncomeLevel: income,
  SubList:     sub,
 }
}

现今社会的等级划分结果如下:

代码语言:javascript复制
[ Name: 高层领导干部, Role: 社会上层, IncomeLevel: 1000000000 ]
[ Name: 中层领导干部, Role: 中上层, IncomeLevel: 10000000 ]
[ Name: 小企业主, Role: 中中层, IncomeLevel: 10000 ]
[ Name: 农民工程序员, Role: 中下层, IncomeLevel: 1000 ]
[ Name: 失业人员, Role: 底层, IncomeLevel: 100 ]
[ Name: 大企业中层管理人员, Role: 中上层, IncomeLevel: 10000000 ]
[ Name: 办事人员, Role: 中中层, IncomeLevel: 10000 ]
[ Name: 个体服务者, Role: 中下层, IncomeLevel: 1000 ]

0 人点赞