快收藏!最全GO语言实现设计模式【下】

导语| 继上篇【快收藏!最全GO言语完结规划形式】,本文持续列出GO言语完结的经典规划形式示例,每个示例都精心规划,力求契合形式结构,可作为日常编码参考,一起一些常用的规划形式融入了开发实践经验总结,帮助大家在平时作业中灵活运用。

快收藏!最全GO语言实现设计模式【下】

解说器形式

(一)概念

解说器形式用于描绘怎么运用面向目标言语构成一个简略的言语解说器。在某些情况下,为了更好地描绘某一些特定类型的问题,咱们能够创立一种新的言语,这种言语具有自己的表达式和结构,即文法规矩,这些问题的实例将对应为该言语中的语句。此刻,能够运用解说器形式来规划这种新的言语。对解说器形式的学习能够加深咱们对面向目标思想的了解,而且掌握编程言语中文法规矩的解说进程。

(二)示例

界说一个解析特征值的语句解说器,供给是否包含特征值的完结表达式,并供给或表达式与且表达式,一起,生成南极洲特征判别表达式,及美国人特征判别表达式,终究测验程序依据目标特征值描绘,经过表达式判别是否为真。

  • 特征值解说器

package interpreter
import "strings"
// Expression 表达式接口,包含一个解说办法
type Expression interface {
  Interpret(context string) bool
}
// terminalExpression 完结符表达式,判别表达式中是否包含匹配数据
type terminalExpression struct {
  matchData string
}
func NewTerminalExpression(matchData string) *terminalExpression {
  return &terminalExpression{matchData: matchData}
}
// Interpret 判别是否包含匹配字符
func (t *terminalExpression) Interpret(context string) bool {
  if strings.Contains(context, t.matchData) {
    return true
  }
  return false
}
// orExpression 或表达式
type orExpression struct {
  left, right Expression
}
func NewOrExpression(left, right Expression) *orExpression {
  return &orExpression{
    left:  left,
    right: right,
  }
}
func (o *orExpression) Interpret(context string) bool {
  return o.left.Interpret(context) || o.right.Interpret(context)
}
// andExpression 与表达式
type andExpression struct {
  left, right Expression
}
func NewAndExpression(left, right Expression) *andExpression {
  return &andExpression{
    left:  left,
    right: right,
  }
}
func (o *andExpression) Interpret(context string) bool {
  return o.left.Interpret(context) && o.right.Interpret(context)
}
  • 测验程序

package interpreter
import (
  "fmt"
  "testing"
)
func TestInterpreter(t *testing.T) {
  isAntarcticaExpression := generateCheckAntarcticaExpression()
  // 大洲描绘1
  continentDescription1 := "此大洲生活着很多企鹅,全年低温,而且伴随着有暴风雪"
  fmt.Printf("%s,是否是南极洲?%t\n", continentDescription1, isAntarcticaExpression.Interpret(continentDescription1))
  // 大洲描绘2
  continentDescription2 := "此大洲生活着狮子,全年高温多雨"
  fmt.Printf("%s,是否是南极洲?%t\n", continentDescription2, isAntarcticaExpression.Interpret(continentDescription2))
  isAmericanExpression := generateCheckAmericanExpression()
  peopleDescription1 := "此人生活在北美洲的黑人,说着英语,持有美国绿卡"
  fmt.Printf("%s,是否是美国人?%t\n", peopleDescription1, isAmericanExpression.Interpret(peopleDescription1))
  peopleDescription2 := "此人生活在欧洲,说着英语,是欧洲议会议员"
  fmt.Printf("%s,是否是南极洲?%t\n", peopleDescription2, isAmericanExpression.Interpret(peopleDescription2))
}
// generateCheckAntarcticaExpression 生成校验是否是南极洲表达式
func generateCheckAntarcticaExpression() Expression {
  // 判别南极洲的动物,或联系
  animalExpression := NewOrExpression(NewTerminalExpression("企鹅"),
    NewTerminalExpression("蓝鲸"))
  // 判别南极洲的气候,与联系
  weatherExpression := NewAndExpression(NewTerminalExpression("低温"),
    NewTerminalExpression("暴风雪"))
  // 终究回来动物与气候的与联系
  return NewAndExpression(animalExpression, weatherExpression)
}
// generateCheckAmericanExpression 生成查看美国人表达式
func generateCheckAmericanExpression() Expression {
  // 人种判别,或联系
  raceExpression := NewOrExpression(NewTerminalExpression("白人"),
    NewTerminalExpression("黑人"))
  // 生活办法,与联系
  lifeStyleExpression := NewAndExpression(NewTerminalExpression("英语"),
    NewTerminalExpression("北美洲"))
  // 身份,与联系
  identityExpression := NewAndExpression(lifeStyleExpression, NewTerminalExpression("美国绿卡"))
  return NewAndExpression(raceExpression, identityExpression)
}
  • 运转成果

=== RUN   TestInterpreter
此大洲生活着很多企鹅,全年低温,而且伴随着有暴风雪,是否是南极洲?true
此大洲生活着狮子,全年高温多雨,是否是南极洲?false
此人生活在北美洲的黑人,说着英语,持有美国绿卡,是否是美国人?true
此人生活在欧洲,说着英语,是欧洲议会议员,是否是美国人?false
--- PASS: TestInterpreter (0.00s)
PASS

快收藏!最全GO语言实现设计模式【下】

适配器形式

(一)概念

适配器形式是一种结构型规划形式,它能使接口不兼容的目标能够彼此合作。

适配器可担任两个目标间的封装器,它会接纳关于一个目标的调用, 并将其转换为另一个目标可辨认的格式和接口。

(二)示例

经过充电宝给不同充电接口的手机充电是一个十分契合适配器形式特征的生活示例;一般充电宝供给USB电源输出接口,手机充电输入接口则分为两类一是苹果手机的lightning接口,另一类是安卓手机的typeC接口,这两类接口都需求经过适配电源线连接充电宝的USB接口,这儿USB接口就相当于充电宝的通用接口,lightning或typeC接口要想充电需求经过充电线适配。

  • 手机充电插头

package adapter
import "fmt"
// HuaweiPlug 华为手机充电插槽接口
type HuaweiPlug interface {
  ConnectTypeC() string
}
// HuaweiPhone 华为系列手机
type HuaweiPhone struct {
  model string
}
// NewHuaweiPhone 华为手机创立办法
func NewHuaweiPhone(model string) *HuaweiPhone {
  return &HuaweiPhone{
    model: model,
  }
}
// ConnectTypeC 华为手机TypeC充电插槽
func (h *HuaweiPhone) ConnectTypeC() string {
  return fmt.Sprintf("%v connect typeC plug", h.model)
}
// ApplePlug 苹果手机充电插槽
type ApplePlug interface {
  ConnectLightning() string
}
// IPhone 苹果系列手机
type IPhone struct {
  model string
}
// NewIPhone 苹果手机创立办法
func NewIPhone(model string) *IPhone {
  return &IPhone{
    model: model,
  }
}
// ConnectLightning 苹果手机Lightning充电插槽
func (i *IPhone) ConnectLightning() string {
  return fmt.Sprintf("%v connect lightning plug", i.model)
}
  • 充电宝适配器

package adapter
import "fmt"
// CommonPlug 通用的USB电源插槽
type CommonPlug interface {
  ConnectUSB() string
}
// HuaweiPhonePlugAdapter 华为TypeC充电插槽适配通用USB充电插槽
type HuaweiPhonePlugAdapter struct {
  huaweiPhone HuaweiPlug
}
// NewHuaweiPhonePlugAdapter 创立华为手机适配USB充电插槽适配器
func NewHuaweiPhonePlugAdapter(huaweiPhone HuaweiPlug) *HuaweiPhonePlugAdapter {
  return &HuaweiPhonePlugAdapter{
    huaweiPhone: huaweiPhone,
  }
}
// ConnectUSB 链接USB
func (h *HuaweiPhonePlugAdapter) ConnectUSB() string {
  return fmt.Sprintf("%v adapt to usb ", h.huaweiPhone.ConnectTypeC())
}
// ApplePhonePlugAdapter 苹果Lightning充电插槽适配通用USB充电插槽
type ApplePhonePlugAdapter struct {
  iPhone ApplePlug
}
// NewApplePhonePlugAdapter 创立苹果手机适配USB充电插槽适配器
func NewApplePhonePlugAdapter(iPhone ApplePlug) *ApplePhonePlugAdapter {
  return &ApplePhonePlugAdapter{
    iPhone: iPhone,
  }
}
// ConnectUSB 链接USB
func (a *ApplePhonePlugAdapter) ConnectUSB() string {
  return fmt.Sprintf("%v adapt to usb ", a.iPhone.ConnectLightning())
}
// PowerBank 充电宝
type PowerBank struct {
  brand string
}
// Charge 支撑通用USB接口充电
func (p *PowerBank) Charge(plug CommonPlug) string {
  return fmt.Sprintf("%v power bank connect usb plug, start charge for %v", p.brand, plug.ConnectUSB())
}
  • 测验程序

package adapter
import (
  "fmt"
  "testing"
)
func TestAdapter (t *testing.T) {
  huaweiMate40Pro := NewHuaweiPhone("华为 mate40 pro")
  iphone13MaxPro := NewIPhone("苹果 iphone13 pro max")
  powerBank := &PowerBank{"飞利浦"}
  fmt.Println(powerBank.Charge(NewHuaweiPhonePlugAdapter(huaweiMate40Pro)))
  fmt.Println(powerBank.Charge(NewApplePhonePlugAdapter(iphone13MaxPro)))
}
  • 运转成果

=== RUN   TestAdapter
飞利浦 power bank connect usb plug, start charge for 华为 mate40 pro connect typeC plug adapt to usb 
飞利浦 power bank connect usb plug, start charge for 苹果 iphone13 pro max connect lightning plug adapt to usb 
--- PASS: TestAdapter (0.00s)
PASS

快收藏!最全GO语言实现设计模式【下】

桥接形式

(一)概念

桥接是一种结构型规划形式,可将业务逻辑或一个大类拆分为不同的层次结构, 从而能独立地进行开发。

层次结构中的榜首层(一般称为笼统部分)将包含对第二层 (完结部分) 目标的引证。笼统部分将能将一些(有时是绝大部分)对自己的调用委派给完结部分的目标。一切的完结部分都有一个通用接口,因而它们能在笼统部分内部彼此替换。

简略的说,一个事物存在多个维度的改变点,每一个维度都笼统出一个接口,事物引证这些接口完结全体行为逻辑,而每一个接口都能够存在多个改变的完结。

更简略的一句话:依赖接口编程。

(二)示例

关于一段阅历的描绘,阅历就可能有多种完结,比方旅行阅历,探险阅历这相当于榜首层次的类结构,一起描绘旅行阅历或探险阅历又包含多个维度,比方怎么抵达目的地,在目的地展开了什么活动等,抵达目的地有很多种办法,比方飞机、火车、轿车等;展开的活动又依据地址不同而不同,海滨能够冲浪,山地能够攀岩,荒漠能够步行穿越等;这两个维度的改变点关于描绘阅历来说相当于第二层次类完结,经过接口被榜首层次引证。

这儿关于阅历描绘存在三个维度的改变,

1.阅历自身的两个完结:旅行阅历与探险阅历。

2.交通办法的两个完结:飞机和轿车。

3.展开活动的三个完结:冲浪、攀岩与步行穿越。

假如用一个类层次去完结就需求2*2*3=12个不同的完结类,假如用桥接形式仅需求2+2+3=7个不同的类,而且两种办法的加速度也不一样,比方增加一个交通办法火车,非桥接形式需求增加2*3*3-12=6个完结类,桥接形式2+3+3-7=1个完结类;桥接形式大大增加了类之间组合的灵活性。

  • 交通工具

package bridge
// Traffic 交通工具
type Traffic interface {
   Transport() string
}
// airplane 飞机
type airplane struct{}
// Transport 坐飞机
func (a *airplane) Transport() string {
   return "by airplane"
}
// car 轿车
type car struct{}
// Transport 坐轿车
func (t *car) Transport() string {
   return "by car"
}
  • 目的地

package bridge
import "fmt"
// Location 地址
type Location interface {
  Name() string // 地址称号
  PlaySports() string // 参加运动
}
// namedLocation 被命名的地址,一致引证此类型,声明姓姓名段及获取办法
type namedLocation struct {
  name string
}
// Name 获取地址称号
func (n namedLocation) Name() string {
  return n.name
}
// seaside 海滨
type seaside struct {
  namedLocation
}
// NewSeaside 创立指定姓名的海滨,比方三亚湾
func NewSeaside(name string) *seaside {
  return &seaside{
    namedLocation: namedLocation{
      name: name,
    },
  }
}
// PlaySports 海滨能够冲浪
func (s *seaside) PlaySports() string {
  return fmt.Sprintf("surfing")
}
// mountain 山
type mountain struct {
  namedLocation
}
// NewMountain 创立指定姓名的山,比方泰山
func NewMountain(name string) *mountain {
  return &mountain{
    namedLocation: namedLocation{
      name: name,
    },
  }
}
// PlaySports 能够爬山
func (m *mountain) PlaySports() string {
  return fmt.Sprintf("climbing")
}
// desert 荒漠
type desert struct {
  namedLocation
}
// NewDesert 创立指定姓名的荒漠,比方罗布泊
func NewDesert(name string) *desert {
  return &desert{
    namedLocation: namedLocation{
      name: name,
    },
  }
}
// PlaySports 荒漠能够步行穿越
func (d *desert) PlaySports() string {
  return fmt.Sprintf("trekking")
}
  • 阅历描绘

package bridge
import "fmt"
// Experience 阅历
type Experience interface {
  Describe() string // 描绘阅历
}
// travelExperience 旅行阅历
type travelExperience struct {
  subject  string
  traffic  Traffic
  location Location
}
// NewTravelExperience 创立旅行阅历,包含主题、交通办法、地址
func NewTravelExperience(subject string, traffic Traffic, location Location) *travelExperience {
  return &travelExperience{
    subject:  subject,
    traffic:  traffic,
    location: location,
  }
}
// Describe 描绘旅行阅历
func (t *travelExperience) Describe() string {
  return fmt.Sprintf("%s is to %s %s and %s", t.subject, t.location.Name(), t.traffic.Transport(), t.location.PlaySports())
}
// adventureExperience 探险阅历
type adventureExperience struct {
  survivalTraining string
  travelExperience
}
// NewAdventureExperience 创立探险阅历,包含探险需求的培训,其他的与路由参数相似
func NewAdventureExperience(training string, subject string, traffic Traffic, location Location) *adventureExperience {
  return &adventureExperience{
    survivalTraining: training,
    travelExperience: *NewTravelExperience(subject, traffic, location),
  }
}
// Describe 描绘探险阅历
func (a *adventureExperience) Describe() string {
  return fmt.Sprintf("after %s, %s", a.survivalTraining, a.travelExperience.Describe())
}
  • 测验程序

package bridge
import (
   "fmt"
   "testing"
)
func TestBridge(t *testing.T) {
   // 坐飞机去三亚度蜜月
   honeymoonTravel := NewTravelExperience("honeymoon", new(airplane), NewSeaside("SanyaYalongBay"))
   fmt.Println(honeymoonTravel.Describe())
   // 坐车去泰山毕业旅行
   graduationTrip := NewTravelExperience("graduationTrip", new(car), NewMountain("Tarzan"))
   fmt.Println(graduationTrip.Describe())
   // 户外生存培训后,坐车去罗布泊,步行穿越
   desertAdventure := NewAdventureExperience("wilderness survival training", "adventure", new(car), NewDesert("Lop Nor"))
   fmt.Println(desertAdventure.Describe())
}
  • 运转成果

=== RUN   TestBridge
honeymoon is to SanyaYalongBay by airplane and surfing
graduationTrip is to Tarzan by car and climbing
after wilderness survival training, adventure is to Lop Nor by car and trekking
--- PASS: TestBridge (0.00s)
PASS

快收藏!最全GO语言实现设计模式【下】

组合形式

(一)概念

组合是一种结构型规划形式,你能够运用它将目标组合成树状结构,而且能像运用独立目标一样运用它们。

关于绝大多数需求生成树状结构的问题来说,组合都是十分受欢迎的解决方案。组合最主要的功用是在整个树状结构上递归调用办法并对成果进行汇总。

(二)示例

一般来说一个地区核算人口或经济总量,总是经过行政区划一层层上报汇总得出成果,区镇是最低一级行政区划,需求落实核算人口及经济总量的作业,再上一级行政区划需求将所辖区镇的数据汇总核算,以此类推每一级行政区划都需求核算人口与经济总量,就像一个倒过来的树状结构,各级行政区划一致的组件接口是核算人口与经济总量,区镇相当于最底层的叶子节点,中心级别行政区划相当于组合节点;下面代码以苏州市为例;

  • 组件接口

package composite
// Region 行政区,作为组合形式component接口
type Region interface {
   Name() string    // 称号
   Population() int //人口
   GDP() float64    // gdp
}
  • 区镇完结

package composite
// town 区镇,组合形式中相当于叶子节点
type town struct {
  name       string
  population int
  gdp        float64
}
// NewTown 创立区镇,依据称号、人口、GDP
func NewTown(name string, population int, gdp float64) *town {
  return &town{
    name:       name,
    population: population,
    gdp:        gdp,
  }
}
func (c *town) Name() string {
  return c.name
}
func (c *town) Population() int {
  return c.population
}
func (c *town) GDP() float64 {
  return c.gdp
}
  • 县市地市完结

package composite
// cities 市,包含县市或者地市,组合形式中相当于composite
type cities struct {
  name    string
  regions map[string]Region
}
// NewCities 创立一个市
func NewCities(name string) *cities {
  return &cities{
    name:    name,
    regions: make(map[string]Region),
  }
}
func (c *cities) Name() string {
  return c.name
}
func (c *cities) Population() int {
  sum := 0
  for _, r := range c.regions {
    sum += r.Population()
  }
  return sum
}
func (c *cities) GDP() float64 {
  sum := 0.0
  for _, r := range c.regions {
    sum += r.GDP()
  }
  return sum
}
// Add 增加多个行政区
func (c *cities) Add(regions ...Region) {
  for _, r := range regions {
    c.regions[r.Name()] = r
  }
}
// Remove 递归删去行政区
func (c *cities) Remove(name string) {
  for n, r := range c.regions {
    if n == name {
      delete(c.regions, name)
      return
    }
    if city, ok := r.(*cities); ok {
      city.Remove(name)
    }
  }
}
func (c *cities) Regions() map[string]Region {
  return c.regions
}
  • 测验程序

package composite
import (
   "fmt"
   "testing"
)
func TestComposite(t *testing.T) {
   gusu := NewTown("姑苏区", 100, 2000.00)
   fmt.Println(ShowRegionInfo(gusu))
   wuzhong := NewTown("吴中区", 150, 2600.00)
   fmt.Println(ShowRegionInfo(wuzhong))
   huqiu := NewTown("虎丘区", 80, 1800.00)
   fmt.Println(ShowRegionInfo(huqiu))
   kunshan := NewCities("昆山市")
   kunshan.Add(NewTown("玉山镇", 60, 1200.00),
      NewTown("周庄镇", 68, 1900.00),
      NewTown("花桥镇", 78, 2200.00))
   fmt.Println(ShowRegionInfo(kunshan))
   changshu := NewCities("常熟市")
   changshu.Add(NewTown("沙家浜镇", 55, 1100.00),
      NewTown("古里镇", 59, 1300.00),
      NewTown("辛庄镇", 68, 2100.00))
   fmt.Println(ShowRegionInfo(changshu))
   suzhou := NewCities("苏州市")
   suzhou.Add(gusu, wuzhong, huqiu, kunshan, changshu)
   fmt.Println(ShowRegionInfo(suzhou))
}
func ShowRegionInfo(region Region) string {
   return fmt.Sprintf("%s, 人口:%d万, GDP:%.2f亿", region.Name(), region.Population(), region.GDP())
}
  • 运转成果

=== RUN   TestComposite
姑苏区, 人口:100万, GDP:2000.00亿
吴中区, 人口:150万, GDP:2600.00亿
虎丘区, 人口:80万, GDP:1800.00亿
昆山市, 人口:206万, GDP:5300.00亿
常熟市, 人口:182万, GDP:4500.00亿
苏州市, 人口:718万, GDP:16200.00亿
--- PASS: TestComposite (0.00s)
PASS

快收藏!最全GO语言实现设计模式【下】

装修形式

(一)概念

装修是一种结构规划形式,答应你经过将目标放入特殊封装目标中来为原目标增加新的行为。

因为目标目标和装修器遵从同一接口,因而你可用装修来对目标进行无限次的封装。成果目标将获得一切封装器叠加而来的行为。

(二)示例

地铁进站的进程一般情况下只需求买票,检票进站,可是假如你是带行李,就需求进行安全查看,假如是疫情时期,就需求进行疫情防护查看,比方戴口罩、丈量体温等,这儿买票进站相当于通用进站流程,安检及防疫查看就相当于加强的润饰行为。

  • 润饰器完结

package decorator
import "fmt"
// Station 车站,润饰器形式一致接口
type Station interface {
  Enter() string // 进站
}
// subwayStation 地铁站
type subwayStation struct {
  name string
}
// NewSubwayStation 创立指定站名地铁站
func NewSubwayStation(name string) *subwayStation {
  return &subwayStation{
    name: name,
  }
}
// Enter 进地铁站
func (s *subwayStation) Enter() string {
  return fmt.Sprintf("买票进入%s地铁站。", s.name)
}
// securityCheckDecorator 进站安检润饰器
type securityCheckDecorator struct {
  station Station
}
func NewSecurityCheckDecorator(station Station) *securityCheckDecorator {
  return &securityCheckDecorator{
    station: station,
  }
}
func (s *securityCheckDecorator) Enter() string {
  return "行李经过安检;" + s.station.Enter()
}
// epidemicProtectionDecorator 进站疫情防护润饰器
type epidemicProtectionDecorator struct {
  station Station
}
func NewEpidemicProtectionDecorator(station Station) *epidemicProtectionDecorator {
  return &epidemicProtectionDecorator{
    station: station,
  }
}
func (e *epidemicProtectionDecorator) Enter() string {
  return "丈量体温,佩戴口罩;" + e.station.Enter()
}
  • 测验代码

package decorator
import (
   "fmt"
   "testing"
)
func TestDecorator(t *testing.T) {
   xierqiStation := NewSubwayStation("西二旗")
   fmt.Println(EnhanceEnterStationProcess(xierqiStation, false, false).Enter())
   fmt.Println(EnhanceEnterStationProcess(xierqiStation, true, false).Enter())
   fmt.Println(EnhanceEnterStationProcess(xierqiStation, true, true).Enter())
}
// EnhanceEnterStationProcess 依据是否有行李,是否处于疫情,增加进站流程
func EnhanceEnterStationProcess(station Station, hasLuggage bool, hasEpidemic bool) Station {
   if hasLuggage {
      station = NewSecurityCheckDecorator(station)
   }
   if hasEpidemic {
      station = NewEpidemicProtectionDecorator(station)
   }
   return station
}
  • 运转成果

=== RUN   TestDecorator
买票进入西二旗地铁站。
行李经过安检;买票进入西二旗地铁站。
丈量体温,佩戴口罩;行李经过安检;买票进入西二旗地铁站。
--- PASS: TestDecorator (0.00s)
PASS

快收藏!最全GO语言实现设计模式【下】

外观形式

(一)概念

外观是一种结构型规划形式,能为杂乱体系、程序库或框架供给一个简略 (但有限) 的接口。

虽然外观形式降低了程序的全体杂乱度,但它一起也有助于将不需求的依赖移动到同一个方位。

(二)示例

用户在淘宝电商体系买产品时,只需求选好产品在结算页点击提交即可完结下单;在客户端体系仅需求一个创立订单的办法,可是整个订单的生成需求很多进程,比方查询用户配送地址,查询产品价格,运用优惠券,扣减产品库存,付出相应价钱等。

  • 淘宝电商体系

package facade
import "fmt"
// TaobaoFacade 淘宝网站门面,在淘宝网站下单涉及到多个体系合作调用,包含用户体系,产品体系,优惠券体系,库存体系,付出体系,终究生成订单
type TaobaoFacade struct {
  userService    *UserService
  productService *ProductService
  couponService  *CouponService
  stockService   *StockService
  paymentService *PaymentService
}
// NewTaobaoFacade 创立淘宝网站
func NewTaobaoFacade() *TaobaoFacade {
  return &TaobaoFacade{
    userService: &UserService{},
    productService: &ProductService{
      products: map[string]float64{"笔记本电脑": 6666.66},
    },
    couponService:  &CouponService{},
    stockService:   &StockService{},
    paymentService: &PaymentService{},
  }
}
// CreateOrder 依据用户名,产品名,产品数量生成购买订单
func (t *TaobaoFacade) CreateOrder(userName string, productName string, count int) string {
  // 运用优惠券
  couponInfo := t.couponService.useCoupon()
  // 扣减库存
  stockInfo := t.stockService.decreaseFor(productName, count)
  // 核算产品总价
  sumPrice := t.productService.getProductPrice(productName) * float64(count)
  // 付出价格
  payInfo := t.paymentService.pay(sumPrice)
  return fmt.Sprintf("用户%s,购买了%d件%s产品,%s,%s,%s,送货到%s", userName, count, productName, couponInfo,
    stockInfo, payInfo, t.userService.getUserAddress(userName))
}
// UserService 用户体系
type UserService struct{}
func (u *UserService) getUserAddress(userName string) string {
  return fmt.Sprintf("%s地址是:北京市海淀区中关村大街,1号院2号楼3单元402", userName)
}
// ProductService 产品体系
type ProductService struct {
  products map[string]float64
}
func (p *ProductService) getProductPrice(productName string) float64 {
  return p.products[productName]
}
// CouponService 优惠券体系
type CouponService struct{}
func (c *CouponService) useCoupon() string {
  return "运用满100减20优惠券"
}
// StockService 库存体系
type StockService struct{}
func (s *StockService) decreaseFor(productName string, count int) string {
  return fmt.Sprintf("扣减%d件%s产品库存", count, productName)
}
// PaymentService 付出体系
type PaymentService struct{}
func (p *PaymentService) pay(amount float64) string {
  return fmt.Sprintf("付出金额%.2f", amount)
}
  • 测验程序

package facade
import (
   "fmt"
   "testing"
)
func TestFacade(t *testing.T) {
   // 经过门面形式,隐藏下单进程中,后端多个体系的杂乱交互
   taobao := NewTaobaoFacade()
   fmt.Println(taobao.CreateOrder("张三", "笔记本电脑", 1))
}
  • 运转成果

=== RUN   TestFacade
用户张三,购买了1件笔记本电脑产品,运用满100减20优惠券,扣减1件笔记本电脑产品库存,付出金额6666.66,送货到张三地址是:北京市海淀区中关村大街,1号院2号楼3单元402
--- PASS: TestFacade (0.00s)
PASS

快收藏!最全GO语言实现设计模式【下】

享元形式

(一)概念

享元是一种结构型规划形式,它答应你在耗费少量内存的情况下支撑很多目标。

形式经过共享多个目标的部分状况来完结上述功用。换句话来说,享元会将不同目标的相同数据进行缓存以节约内存。

(二)示例

北京出租车调度体系,需求每隔一分钟记录一下全市出租车的方位信息,假设为了提高体系响应速度,近一天的数据需求存储在内存中,每个方位信息包含出租车辆信息及方位信息,方位信息在体系中就是一个(x,y)坐标,车辆信息包含车的号牌,颜色,品牌和所属公司,在调度体系存储的出租车行驶轨道中,方位是实时在改变的,但车辆信息就能够经过享元形式共用一个目标引证,来减少内存耗费。

  • 出租车享元目标

package flyweight
import (
  "fmt"
)
// Taxi 出租车,享元目标,保存不变的内涵特点信息
type Taxi struct {
  licensePlate string // 车牌
  color        string // 颜色
  brand        string // 轿车品牌
  company      string // 所属公司
}
// LocateFor 获取定位信息
func (t *Taxi) LocateFor(monitorMap string, x, y int) string {
  return fmt.Sprintf("%s,关于车牌号%s,%s,%s品牌,所属%s公司,定位(%d,%d)", monitorMap,
    t.licensePlate, t.color, t.brand, t.company, x, y)
}
// taxiFactoryInstance 出租车工厂单例
var taxiFactoryInstance = &TaxiFactory{
  taxis: make(map[string]*Taxi),
}
// GetTaxiFactory 获取出租车工厂单例
func GetTaxiFactory() *TaxiFactory {
  return taxiFactoryInstance
}
// TaxiFactory 出租车工厂类
type TaxiFactory struct {
  taxis map[string]*Taxi // key为车牌号
}
// getTaxi 获取出租车
func (f *TaxiFactory) getTaxi(licensePlate, color, brand, company string) *Taxi {
  if _, ok := f.taxis[licensePlate]; !ok {
    f.taxis[licensePlate] = &Taxi{
      licensePlate: licensePlate,
      color:        color,
      brand:        brand,
      company:      company,
    }
  }
  return f.taxis[licensePlate]
}
  • 出租车调度体系

package flyweight
import "bytes"
// TaxiPosition 出租车方位信息 x,y为外在数据信息,taxi为内涵数据信息(享元目标)
type TaxiPosition struct {
  x    int
  y    int
  taxi *Taxi
}
func NewTaxiPosition(taxi *Taxi, x, y int) *TaxiPosition {
  return &TaxiPosition{
    taxi: taxi,
    x:    x,
    y:    y,
  }
}
// LocateFor 定位信息
func (p *TaxiPosition) LocateFor(monitorMap string) string {
  return p.taxi.LocateFor(monitorMap, p.x, p.y)
}
// TaxiDispatcher 出租车调度体系
type TaxiDispatcher struct {
  name   string
  traces map[string][]*TaxiPosition // 存储出租车当天轨道信息,key为车牌号
}
func NewTaxiDispatcher(name string) *TaxiDispatcher {
  return &TaxiDispatcher{
    name:   name,
    traces: make(map[string][]*TaxiPosition),
  }
}
// AddTrace 增加轨道
func (t *TaxiDispatcher) AddTrace(licensePlate, color, brand, company string, x, y int) {
  taxi := GetTaxiFactory().getTaxi(licensePlate, color, brand, company)
  t.traces[licensePlate] = append(t.traces[licensePlate], NewTaxiPosition(taxi, x, y))
}
// ShowTraces 显现轨道
func (t *TaxiDispatcher) ShowTraces(licensePlate string) string {
  bytesBuf := bytes.Buffer{}
  for _, trace := range t.traces[licensePlate] {
    bytesBuf.WriteString(trace.LocateFor(t.name))
    bytesBuf.WriteByte('\n')
  }
  return bytesBuf.String()
}
  • 测验程序

package flyweight
import (
   "fmt"
   "testing"
)
func TestFlyweight(t *testing.T) {
   dispatcher := NewTaxiDispatcher("北京市出租车调度体系")
   dispatcher.AddTrace("京B.123456", "黄色", "北京现代", "北汽", 10, 20)
   dispatcher.AddTrace("京B.123456", "黄色", "北京现代", "北汽", 20, 30)
   dispatcher.AddTrace("京B.123456", "黄色", "北京现代", "北汽", 30, 40)
   dispatcher.AddTrace("京B.123456", "黄色", "北京现代", "北汽", 40, 50)
   dispatcher.AddTrace("京B.567890", "赤色", "一汽大众", "首汽", 20, 40)
   dispatcher.AddTrace("京B.567890", "赤色", "一汽大众", "首汽", 50, 50)
   fmt.Println(dispatcher.ShowTraces("京B.123456"))
   fmt.Println(dispatcher.ShowTraces("京B.567890"))
}
  • 运转成果

=== RUN   TestFlyweight
北京市出租车调度体系,关于车牌号京B.123456,黄色,北京现代品牌,所属北汽公司,定位(10,20)
北京市出租车调度体系,关于车牌号京B.123456,黄色,北京现代品牌,所属北汽公司,定位(20,30)
北京市出租车调度体系,关于车牌号京B.123456,黄色,北京现代品牌,所属北汽公司,定位(30,40)
北京市出租车调度体系,关于车牌号京B.123456,黄色,北京现代品牌,所属北汽公司,定位(40,50)
北京市出租车调度体系,关于车牌号京B.567890,赤色,一汽大众品牌,所属首汽公司,定位(20,40)
北京市出租车调度体系,关于车牌号京B.567890,赤色,一汽大众品牌,所属首汽公司,定位(50,50)
--- PASS: TestFlyweight (0.00s)
PASS

快收藏!最全GO语言实现设计模式【下】

署理形式

(一)概念

署理是一种结构型规划形式,让你能供给实在服务目标的替代品给客户端运用。署理接纳客户端的恳求并进行一些处理 (访问操控和缓存等), 然后再将恳求传递给服务目标。

署理目标具有和服务目标相同的接口,这使得当其被传递给客户端时可与实在目标互换。

润饰与署理是十分相似的规划形式,都是基于组合规划准则,也就是说一个目标应该将部分作业委派给另一个目标。但两者之间不同点我认为是,润饰器形式总是要履行服务目标,关于履行之前或履行之后成果进行加强,服务目标基本是客户端创立好再嵌套外层的润饰目标;而署理形式不一定履行服务目标,有可能经过缓存,延迟加载等没有访问服务目标,一起服务目标什么时候创立也是由署理类决议的。

(二)示例

房子中介署理帮助房东卖房子,这个进程就是一个署理形式的进程,中介会搜集尽量多的卖房信息,并经过各种渠道发布,一起中介会随时带客户看房,并开始参议价格,假如达到开始购买意向,才会约房东评论房子价格,终究签约卖房;房子中介与房东都完结卖房接口,中介会提早坐一些前期作业,假如都没问题,才会约房东履行真正的签约卖房流程。

  • 房子中介卖房

package proxy
import (
  "bytes"
  "fmt"
)
// HouseSeller 房子出售者
type HouseSeller interface {
  SellHouse(address string, buyer string) string
}
// houseProxy 房产中介署理
type houseProxy struct {
  houseSeller HouseSeller
}
func NewHouseProxy(houseSeller HouseSeller) *houseProxy {
  return &houseProxy{
    houseSeller: houseSeller,
  }
}
// SellHouse 中介卖房,看房->开始谈价->终究和房东签协议
func (h *houseProxy) SellHouse(address string, buyer string) string {
  buf := bytes.Buffer{}
  buf.WriteString(h.viewHouse(address, buyer) + "\n")
  buf.WriteString(h.preBargain(address, buyer) + "\n")
  buf.WriteString(h.houseSeller.SellHouse(address, buyer))
  return buf.String()
}
// viewHouse 看房介绍基本情况
func (h *houseProxy) viewHouse(address string, buyer string) string {
  return fmt.Sprintf("带买家%s看坐落%s的房子,并介绍基本情况", buyer, address)
}
// preBargain 开始沟通价格
func (h *houseProxy) preBargain(address string, buyer string) string {
  return fmt.Sprintf("讨价还价后,开始达到购买意向")
}
// houseOwner 房东
type houseOwner struct{}
// SellHouse 房东卖房,参议价格,签署购房协议
func (h *houseOwner) SellHouse(address string, buyer string) string {
  return fmt.Sprintf("终究参议价格后,与%s签署购买地址为%s的购房协议。", buyer, address)
}
  • 测验程序

package proxy
import (
  "fmt"
  "testing"
)
func TestProxy(t *testing.T) {
  proxy := NewHouseProxy(&houseOwner{})
  fmt.Println(proxy.SellHouse("北京市海淀区中关村大街,2号院1号楼4单元502室", "李四"))
}
  • 运转成果

=== RUN   TestProxy
带买家李四看坐落北京市海淀区中关村大街,2号院1号楼4单元502室的房子,并介绍基本情况
讨价还价后,开始达到购买意向
终究参议价格后,与李四签署购买地址为北京市海淀区中关村大街,2号院1号楼4单元502室的购房协议。
--- PASS: TestProxy (0.00s)
PASS

快收藏!最全GO语言实现设计模式【下】

工厂办法形式

(一)概念

工厂办法形式是一种创立型规划形式,其在父类中供给一个创立目标的办法, 答应子类决议实例化目标的类型。

(二)示例

摊煎饼的小贩需求先摊个煎饼,再卖出去,摊煎饼就能够类比为一个工厂办法,依据顾客的喜爱摊出不同口味的煎饼。

  • 接口

package factorymethod
// Pancake 煎饼
type Pancake interface {
  // ShowFlour 煎饼运用的面粉
  ShowFlour() string
  // Value 煎饼价格
  Value() float32
}
// PancakeCook 煎饼厨师
type PancakeCook interface {
  // MakePancake 摊煎饼
  MakePancake() Pancake
}
// PancakeVendor 煎饼小贩
type PancakeVendor struct {
  PancakeCook
}
// NewPancakeVendor ...
func NewPancakeVendor(cook PancakeCook) *PancakeVendor {
  return &PancakeVendor{
    PancakeCook: cook,
  }
}
// SellPancake 卖煎饼,先摊煎饼,再卖
func (vendor *PancakeVendor) SellPancake() (money float32) {
  return vendor.MakePancake().Value()
}
  • 完结

各种面的煎饼完结

package factorymethod
// cornPancake 玉米面煎饼
type cornPancake struct{}
// NewCornPancake ...
func NewCornPancake() *cornPancake {
  return &cornPancake{}
}
func (cake *cornPancake) ShowFlour() string {
  return "玉米面"
}
func (cake *cornPancake) Value() float32 {
  return 5.0
}
// milletPancake 小米面煎饼
type milletPancake struct{}
func NewMilletPancake() *milletPancake {
  return &milletPancake{}
}
func (cake *milletPancake) ShowFlour() string {
  return "小米面"
}
func (cake *milletPancake) Value() float32 {
  return 8.0
}

制造各种口味煎饼的工厂办法完结

package factorymethod
// cornPancakeCook 制造玉米面煎饼厨师
type cornPancakeCook struct{}
func NewCornPancakeCook() *cornPancakeCook {
  return &cornPancakeCook{}
}
func (cook *cornPancakeCook) MakePancake() Pancake {
  return NewCornPancake()
}
// milletPancakeCook 制造小米面煎饼厨师
type milletPancakeCook struct{}
func NewMilletPancakeCook() *milletPancakeCook {
  return &milletPancakeCook{}
}
func (cook *milletPancakeCook) MakePancake() Pancake {
  return NewMilletPancake()
}
  • 运用

package factorymethod
import (
  "fmt"
  "testing"
)
func TestFactoryMethod(t *testing.T) {
  pancakeVendor := NewPancakeVendor(NewCornPancakeCook())
  fmt.Printf("Corn pancake value is %v\n", pancakeVendor.SellPancake())
  pancakeVendor = NewPancakeVendor(NewMilletPancakeCook())
  fmt.Printf("Millet pancake value is %v\n", pancakeVendor.SellPancake())
}

输出:

=== RUN   TestFactoryMethod
Corn pancake value is 5
Millet pancake value is 8
--- PASS: TestFactoryMethod (0.00s)
PASS

快收藏!最全GO语言实现设计模式【下】

笼统工厂形式

(一)概念

笼统工厂是一种创立型规划形式,它能创立一系列相关的目标,而无需指定其详细类。

笼统工厂界说了用于创立不同产品的接口,但将实践的创立作业留给了详细工厂类。每个工厂类型都对应一个特定的产品变体。

在创立产品时,客户端代码调用的是工厂目标的构建办法,而不是直接调用构造函数 ( new操作符)。因为一个工厂对应一种产品变体,因而它创立的一切产品都可彼此兼容。

客户端代码仅经过其笼统接口与工厂和产品进行交互。该接口答应同一客户端代码与不同产品进行交互。你只需创立一个详细工厂类并将其传递给客户端代码即可。

(二)示例

厨师准备一餐时,会别离做吃的和喝的,依据早、中、晚三餐饮食习惯,会别离制造不同的饮食,厨师就相当于笼统工厂,制造三餐的不同烹饪办法就好比不同笼统工厂的完结。

  • 接口

package abstractfactory
// Cook 厨师接口,笼统工厂
type Cook interface {
  // MakeFood 制造主食
  MakeFood() Food
  // MakeDrink 制造饮品
  MakeDrink() Drink
}
// Food 主食接口
type Food interface {
  // Eaten 被吃
  Eaten() string
}
// Drink 饮品接口
type Drink interface {
  // Drunk 被喝
  Drunk() string
}
  • 完结

三餐不同厨师接口的完结

package abstractfactory
// breakfastCook 早餐厨师
type breakfastCook struct{}
func NewBreakfastCook() *breakfastCook {
  return &breakfastCook{}
}
func (b *breakfastCook) MakeFood() Food {
  return &cakeFood{"切片面包"}
}
func (b *breakfastCook) MakeDrink() Drink {
  return &gruelDrink{"小米粥"}
}
// lunchCook 午饭厨师
type lunchCook struct{}
func NewLunchCook() *lunchCook {
  return &lunchCook{}
}
func (l *lunchCook) MakeFood() Food {
  return &dishFood{"烤全羊"}
}
func (l *lunchCook) MakeDrink() Drink {
  return &sodaDrink{"冰镇可口可乐"}
}
// dinnerCook 晚餐厨师
type dinnerCook struct{}
func NewDinnerCook() *dinnerCook {
  return &dinnerCook{}
}
func (d *dinnerCook) MakeFood() Food {
  return &noodleFood{"大盘鸡拌面"}
}
func (d *dinnerCook) MakeDrink() Drink {
  return &soupDrink{"西红柿鸡蛋汤"}
}

不同吃的

package abstractfactory
import "fmt"
// cakeFood 蛋糕
type cakeFood struct {
  cakeName string
}
func (c *cakeFood) Eaten() string {
  return fmt.Sprintf("%v被吃", c.cakeName)
}
// dishFood 菜肴
type dishFood struct {
  dishName string
}
func (d *dishFood) Eaten() string {
  return fmt.Sprintf("%v被吃", d.dishName)
}
// noodleFood 面条
type noodleFood struct {
  noodleName string
}
func (n *noodleFood) Eaten() string {
  return fmt.Sprintf("%v被吃", n.noodleName)
}

不同喝的

package abstractfactory
import "fmt"
// gruelDrink 粥
type gruelDrink struct {
  gruelName string
}
func (g *gruelDrink) Drunk() string {
  return fmt.Sprintf("%v被喝", g.gruelName)
}
// sodaDrink 汽水
type sodaDrink struct {
  sodaName string
}
func (s *sodaDrink) Drunk() string {
  return fmt.Sprintf("%v被喝", s.sodaName)
}
// soupDrink 汤
type soupDrink struct {
  soupName string
}
func (s *soupDrink) Drunk() string {
  return fmt.Sprintf("%v被喝", s.soupName)
}

运用

package abstractfactory
import (
  "fmt"
  "testing"
)
func TestAbstractFactory(t *testing.T) {
  fmt.Printf("breakfast: %v\n", HaveMeal(NewBreakfastCook()))
  fmt.Printf("lunch: %v\n", HaveMeal(NewLunchCook()))
  fmt.Printf("dinner: %v\n", HaveMeal(NewDinnerCook()))
}
// HaveMeal 吃饭
func HaveMeal(cook Cook) string {
  return fmt.Sprintf("%s %s", cook.MakeFood().Eaten(), cook.MakeDrink().Drunk())
}

输出

=== RUN   TestAbstractFactory
breakfast: 切片面包被吃 小米粥被喝
lunch: 烤全羊被吃 冰镇可口可乐被喝
dinner: 大盘鸡拌面被吃 西红柿鸡蛋汤被喝
--- PASS: TestAbstractFactory (0.00s)
PASS

快收藏!最全GO语言实现设计模式【下】

生成器形式

(一)概念

生成器是一种创立型规划形式,使你能够分进程创立杂乱目标。

与其他创立型形式不同,生成器不要求产品具有通用接口。这使得用相同的创立进程生成不同的产品成为可能。

(二)示例

还是摊煎饼的比方,摊煎饼分为四个进程,1放面糊、2放鸡蛋、3放调料、4放薄脆,经过四个创立进程,制造好一个煎饼,这个摊煎饼的进程就好比煎饼生成器接口,不同生成器的完结就相当于摊不同品类的煎饼,比方正常的煎饼,健康的煎饼(可能用的是粗粮面、柴鸡蛋、非油炸薄脆、不放酱等),生成器接口办法也能够经过参数操控煎饼的大小,比方放两勺面糊,放2个鸡蛋等。

生成器的运用者为了防止每次都调用相同的构建进程,也能够经过包装类固定几种构建进程,生成几类常用的产品,就好像摊煎饼有几类常卖固定成品,比方一般的,加两个鸡蛋的,不要香菜的等等,这几类固定构建进程提早定制好,直接经过简略工厂办法就直接创立,假如用户再需求细粒度的定制构建,再经过生成器创立。

  • 接口

package builder
// Quantity 分量
type Quantity int
const (
  Small  Quantity = 1
  Middle Quantity = 5
  Large  Quantity = 10
)
type PancakeBuilder interface {
  // PutPaste 放面糊
  PutPaste(quantity Quantity)
  // PutEgg 放鸡蛋
  PutEgg(num int)
  // PutWafer 放薄脆
  PutWafer()
  // PutFlavour 放调料 Coriander香菜,Shallot葱 Sauce酱
  PutFlavour(hasCoriander, hasShallot, hasSauce bool)
  // Build 摊煎饼
  Build() *Pancake
}
// Pancake  煎饼
type Pancake struct {
  pasteQuantity Quantity // 面糊分量
  eggNum        int      // 鸡蛋数量
  wafer         string   // 薄脆
  hasCoriander  bool     // 是否放香菜
  hasShallot    bool     // 是否放葱
  hasSauce      bool     // 是否放酱
}
  • 完结

正常煎饼创立器

package builder
type normalPancakeBuilder struct {
  pasteQuantity Quantity // 面糊量
  eggNum        int      // 鸡蛋数量
  friedWafer    string   // 油炸薄脆
  hasCoriander  bool     // 是否放香菜
  hasShallot    bool     // 是否放葱
  hasHotSauce   bool     // 是否放辣味酱
}
func NewNormalPancakeBuilder() *normalPancakeBuilder {
  return &normalPancakeBuilder{}
}
func (n *normalPancakeBuilder) PutPaste(quantity Quantity) {
  n.pasteQuantity = quantity
}
func (n *normalPancakeBuilder) PutEgg(num int) {
  n.eggNum = num
}
func (n *normalPancakeBuilder) PutWafer() {
  n.friedWafer = "油炸的薄脆"
}
func (n *normalPancakeBuilder) PutFlavour(hasCoriander, hasShallot, hasSauce bool) {
  n.hasCoriander = hasCoriander
  n.hasShallot = hasShallot
  n.hasHotSauce = hasSauce
}
func (n *normalPancakeBuilder) Build() *Pancake {
  return &Pancake{
    pasteQuantity: n.pasteQuantity,
    eggNum:        n.eggNum,
    wafer:         n.friedWafer,
    hasCoriander:  n.hasCoriander,
    hasShallot:    n.hasShallot,
    hasSauce:      n.hasHotSauce,
  }
}

健康煎饼创立器

package builder
type healthyPancakeBuilder struct {
  milletPasteQuantity Quantity // 小米面糊量
  chaiEggNum          int      // 柴鸡蛋数量
  nonFriedWafer       string   // 非油炸薄脆
  hasCoriander        bool     // 是否放香菜
  hasShallot          bool     // 是否放葱
}
func NewHealthyPancakeBuilder() *healthyPancakeBuilder {
  return &healthyPancakeBuilder{}
}
func (n *healthyPancakeBuilder) PutPaste(quantity Quantity) {
  n.milletPasteQuantity = quantity
}
func (n *healthyPancakeBuilder) PutEgg(num int) {
  n.chaiEggNum = num
}
func (n *healthyPancakeBuilder) PutWafer() {
  n.nonFriedWafer = "非油炸的薄脆"
}
func (n *healthyPancakeBuilder) PutFlavour(hasCoriander, hasShallot, _ bool) {
  n.hasCoriander = hasCoriander
  n.hasShallot = hasShallot
}
func (n *healthyPancakeBuilder) Build() *Pancake {
  return &Pancake{
    pasteQuantity: n.milletPasteQuantity,
    eggNum:        n.chaiEggNum,
    wafer:         n.nonFriedWafer,
    hasCoriander:  n.hasCoriander,
    hasShallot:    n.hasShallot,
    hasSauce:      false,
  }
}

煎饼生成器的封装类-厨师

package builder
// PancakeCook 摊煎饼师傅
type PancakeCook struct {
  builder PancakeBuilder
}
func NewPancakeCook(builder PancakeBuilder) *PancakeCook {
  return &PancakeCook{
    builder: builder,
  }
}
// SetPancakeBuilder 重新设置煎饼构造器
func (p *PancakeCook) SetPancakeBuilder(builder PancakeBuilder) {
  p.builder = builder
}
// MakePancake 摊一个一般煎饼
func (p *PancakeCook) MakePancake() *Pancake {
  p.builder.PutPaste(Middle)
  p.builder.PutEgg(1)
  p.builder.PutWafer()
  p.builder.PutFlavour(true, true, true)
  return p.builder.Build()
}
// MakeBigPancake 摊一个巨无霸煎饼
func (p *PancakeCook) MakeBigPancake() *Pancake {
  p.builder.PutPaste(Large)
  p.builder.PutEgg(3)
  p.builder.PutWafer()
  p.builder.PutFlavour(true, true, true)
  return p.builder.Build()
}
// MakePancakeForFlavour 摊一个自选调料霸煎饼
func (p *PancakeCook) MakePancakeForFlavour(hasCoriander, hasShallot, hasSauce bool) *Pancake {
  p.builder.PutPaste(Large)
  p.builder.PutEgg(3)
  p.builder.PutWafer()
  p.builder.PutFlavour(hasCoriander, hasShallot, hasSauce)
  return p.builder.Build()
}
  • 运用

package builder
import (
  "fmt"
  "testing"
)
func TestBuilder(t *testing.T) {
  pancakeCook := NewPancakeCook(NewNormalPancakeBuilder())
  fmt.Printf("摊一个一般煎饼 %#v\n", pancakeCook.MakePancake())
  pancakeCook.SetPancakeBuilder(NewHealthyPancakeBuilder())
  fmt.Printf("摊一个健康的加量煎饼 %#v\n", pancakeCook.MakeBigPancake())
}

输出

=== RUN   TestBuilder
摊一个一般煎饼 &builder.Pancake{pasteQuantity:5, eggNum:1, wafer:"油炸的薄脆", hasCoriander:true, hasShallot:true, hasSauce:true}
摊一个健康的加量煎饼 &builder.Pancake{pasteQuantity:10, eggNum:3, wafer:"非油炸的薄脆", hasCoriander:true, hasShallot:true, hasSauce:false}
--- PASS: TestBuilder (0.00s)
PASS

快收藏!最全GO语言实现设计模式【下】

原型形式

(一)概念

原型是一种创立型规划形式,使你能够仿制目标,甚至是杂乱目标,而又无需使代码依赖它们所属的类。

一切的原型类都必须有一个通用的接口, 使得即使在目标所属的详细类未知的情况下也能仿制目标。原型目标能够生成自身的完整副本, 因为相同类的目标能够彼此访问对方的私有成员变量。

(二)示例

纸质文件能够经过复印机轻松复制出多份,设置Paper接口,包含读取文件内容和克隆文件两个办法。一起声明两个类报纸(Newspaper)和简历(Resume)完结了Paper接口,经过复印机(Copier)复印出两类文件的副本,并读取文件副本内容。

  • 接口完结

package prototype
import (
  "bytes"
  "fmt"
  "io"
)
// Paper 纸张,包含读取内容的办法,复制纸张的办法,作为原型形式接口
type Paper interface {
  io.Reader
  Clone() Paper
}
// Newspaper 报纸 完结原型接口
type Newspaper struct {
  headline string
  content  string
}
func NewNewspaper(headline string, content string) *Newspaper {
  return &Newspaper{
    headline: headline,
    content:  content,
  }
}
func (np *Newspaper) Read(p []byte) (n int, err error) {
  buf := bytes.NewBufferString(fmt.Sprintf("headline:%s,content:%s", np.headline, np.content))
  return buf.Read(p)
}
func (np *Newspaper) Clone() Paper {
  return &Newspaper{
    headline: np.headline + "_copied",
    content:  np.content,
  }
}
// Resume 简历 完结原型接口
type Resume struct {
  name       string
  age        int
  experience string
}
func NewResume(name string, age int, experience string) *Resume {
  return &Resume{
    name:       name,
    age:        age,
    experience: experience,
  }
}
func (r *Resume) Read(p []byte) (n int, err error) {
  buf := bytes.NewBufferString(fmt.Sprintf("name:%s,age:%d,experience:%s", r.name, r.age, r.experience))
  return buf.Read(p)
}
func (r *Resume) Clone() Paper {
  return &Resume{
    name:       r.name + "_copied",
    age:        r.age,
    experience: r.experience,
  }
}
  • 运用

package prototype
import (
  "fmt"
  "reflect"
  "testing"
)
func TestPrototype(t *testing.T) {
  copier := NewCopier("云打印机")
  oneNewspaper := NewNewspaper("Go是最好的编程言语", "Go言语十大优势")
  oneResume := NewResume("小明", 29, "5年码农")
  otherNewspaper := copier.copy(oneNewspaper)
  copyNewspaperMsg := make([]byte, 100)
  byteSize, _ := otherNewspaper.Read(copyNewspaperMsg)
  fmt.Println("copyNewspaperMsg:" + string(copyNewspaperMsg[:byteSize]))
  otherResume := copier.copy(oneResume)
  copyResumeMsg := make([]byte, 100)
  byteSize, _ = otherResume.Read(copyResumeMsg)
  fmt.Println("copyResumeMsg:" + string(copyResumeMsg[:byteSize]))
}
// Copier 复印机
type Copier struct {
  name string
}
func NewCopier(n string) *Copier {
  return &Copier{name: n}
}
func (c *Copier) copy(paper Paper) Paper {
  fmt.Printf("copier name:%v is copying:%v ", c.name, reflect.TypeOf(paper).String())
  return paper.Clone()
}

输出

=== RUN   TestPrototype
copier name:云打印机 is copying:*prototype.Newspaper copyNewspaperMsg:headline:Go是最好的编程言语_copied,content:Go言语十大优势
copier name:云打印机 is copying:*prototype.Resume copyResumeMsg:name:小明_copied,age:29,experience:5年码农
--- PASS: TestPrototype (0.00s)
PASS

快收藏!最全GO语言实现设计模式【下】

单例形式

(一)概念

单例是一种创立型规划形式,让你能够确保一个类只要一个实例,并供给一个访问该实例的大局节点。

单例具有与大局变量相同的优缺点。虽然它们十分有用,但却会破坏代码的模块化特性。

(二)示例

经过地球目标完结单例,earth不能导出,经过TheEarth办法访问大局仅有实例,并经过sync.Once完结多协程下一次加载。

  • 接口完结

package singleton
import "sync"
var once sync.Once
// 不可导出目标
type earth struct {
  desc string
}
func (e *earth) String() string {
  return e.desc
}
// theEarth 地球单实例
var theEarth *earth
// TheEarth 获取地球单实例
func TheEarth() *earth {
  if theEarth == nil {
    once.Do(func() {
      theEarth = &earth{
        desc: "美丽的地球,孕育了生命。",
      }
    })
  }
  return theEarth
}
  • 运用

package singleton
import (
  "fmt"
  "testing"
)
func TestSingleton(t *testing.T) {
  fmt.Println(TheEarth().String())
}

输出

=== RUN   TestSingleton
美丽的地球,孕育了生命。
--- PASS: TestSingleton (0.00s)
PASS

快收藏!最全GO语言实现设计模式【下】

阅览原文