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


    点个关注👆跟腾讯工程师学技术

    bfea4d1bece8280e9af580594eb4b693.png

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

    46cfa9d0b62b2c55a62b210154f7974b.jpeg

    解释器模式

    (一)概念

    解释器模式用于描述如何使用面向对象语言构成一个简单的语言解释器。在某些情况下,为了更好地描述某一些特定类型的问题,我们可以创建一种新的语言,这种语言拥有自己的表达式和结构,即文法规则,这些问题的实例将对应为该语言中的句子。此时,可以使用解释器模式来设计这种新的语言。对解释器模式的学习能够加深我们对面向对象思想的理解,并且掌握编程语言中文法规则的解释过程。

    (二)示例

    定义一个解析特征值的语句解释器,提供是否包含特征值的终结表达式,并提供或表达式与且表达式,同时,生成南极洲特征判断表达式,及美国人特征判断表达式,最后测试程序根据对象特征值描述,通过表达式判断是否为真。

    • 特征值解释器

    1. package interpreter
    2. import "strings"
    3. // Expression 表达式接口,包含一个解释方法
    4. type Expression interface {
    5. Interpret(context string) bool
    6. }
    7. // terminalExpression 终结符表达式,判断表达式中是否包含匹配数据
    8. type terminalExpression struct {
    9. matchData string
    10. }
    11. func NewTerminalExpression(matchData string) *terminalExpression {
    12. return &terminalExpression{matchData: matchData}
    13. }
    14. // Interpret 判断是否包含匹配字符
    15. func (t *terminalExpression) Interpret(context string) bool {
    16. if strings.Contains(context, t.matchData) {
    17. return true
    18. }
    19. return false
    20. }
    21. // orExpression 或表达式
    22. type orExpression struct {
    23. left, right Expression
    24. }
    25. func NewOrExpression(left, right Expression) *orExpression {
    26. return &orExpression{
    27. left: left,
    28. right: right,
    29. }
    30. }
    31. func (o *orExpression) Interpret(context string) bool {
    32. return o.left.Interpret(context) || o.right.Interpret(context)
    33. }
    34. // andExpression 与表达式
    35. type andExpression struct {
    36. left, right Expression
    37. }
    38. func NewAndExpression(left, right Expression) *andExpression {
    39. return &andExpression{
    40. left: left,
    41. right: right,
    42. }
    43. }
    44. func (o *andExpression) Interpret(context string) bool {
    45. return o.left.Interpret(context) && o.right.Interpret(context)
    46. }
    • 测试程序

    1. package interpreter
    2. import (
    3. "fmt"
    4. "testing"
    5. )
    6. func TestInterpreter(t *testing.T) {
    7. isAntarcticaExpression := generateCheckAntarcticaExpression()
    8. // 大洲描述1
    9. continentDescription1 := "此大洲生活着大量企鹅,全年低温,并且伴随着有暴风雪"
    10. fmt.Printf("%s,是否是南极洲?%t\n", continentDescription1, isAntarcticaExpression.Interpret(continentDescription1))
    11. // 大洲描述2
    12. continentDescription2 := "此大洲生活着狮子,全年高温多雨"
    13. fmt.Printf("%s,是否是南极洲?%t\n", continentDescription2, isAntarcticaExpression.Interpret(continentDescription2))
    14. isAmericanExpression := generateCheckAmericanExpression()
    15. peopleDescription1 := "此人生活在北美洲的黑人,说着英语,持有美国绿卡"
    16. fmt.Printf("%s,是否是美国人?%t\n", peopleDescription1, isAmericanExpression.Interpret(peopleDescription1))
    17. peopleDescription2 := "此人生活在欧洲,说着英语,是欧洲议会议员"
    18. fmt.Printf("%s,是否是南极洲?%t\n", peopleDescription2, isAmericanExpression.Interpret(peopleDescription2))
    19. }
    20. // generateCheckAntarcticaExpression 生成校验是否是南极洲表达式
    21. func generateCheckAntarcticaExpression() Expression {
    22. // 判断南极洲的动物,或关系
    23. animalExpression := NewOrExpression(NewTerminalExpression("企鹅"),
    24. NewTerminalExpression("蓝鲸"))
    25. // 判断南极洲的天气,与关系
    26. weatherExpression := NewAndExpression(NewTerminalExpression("低温"),
    27. NewTerminalExpression("暴风雪"))
    28. // 最终返回动物与天气的与关系
    29. return NewAndExpression(animalExpression, weatherExpression)
    30. }
    31. // generateCheckAmericanExpression 生成检查美国人表达式
    32. func generateCheckAmericanExpression() Expression {
    33. // 人种判断,或关系
    34. raceExpression := NewOrExpression(NewTerminalExpression("白人"),
    35. NewTerminalExpression("黑人"))
    36. // 生活方式,与关系
    37. lifeStyleExpression := NewAndExpression(NewTerminalExpression("英语"),
    38. NewTerminalExpression("北美洲"))
    39. // 身份,与关系
    40. identityExpression := NewAndExpression(lifeStyleExpression, NewTerminalExpression("美国绿卡"))
    41. return NewAndExpression(raceExpression, identityExpression)
    42. }
    • 运行结果

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

    c2c4ea4d8fceb8ea5ab92f8137501bdd.jpeg

    适配器模式

    (一)概念

    适配器模式是一种结构型设计模式,它能使接口不兼容的对象能够相互合作。

    适配器可担任两个对象间的封装器,它会接收对于一个对象的调用, 并将其转换为另一个对象可识别的格式和接口。

    (二)示例

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

    • 手机充电插头

    1. package adapter
    2. import "fmt"
    3. // HuaweiPlug 华为手机充电插槽接口
    4. type HuaweiPlug interface {
    5. ConnectTypeC() string
    6. }
    7. // HuaweiPhone 华为系列手机
    8. type HuaweiPhone struct {
    9. model string
    10. }
    11. // NewHuaweiPhone 华为手机创建方法
    12. func NewHuaweiPhone(model string) *HuaweiPhone {
    13. return &HuaweiPhone{
    14. model: model,
    15. }
    16. }
    17. // ConnectTypeC 华为手机TypeC充电插槽
    18. func (h *HuaweiPhone) ConnectTypeC() string {
    19. return fmt.Sprintf("%v connect typeC plug", h.model)
    20. }
    21. // ApplePlug 苹果手机充电插槽
    22. type ApplePlug interface {
    23. ConnectLightning() string
    24. }
    25. // IPhone 苹果系列手机
    26. type IPhone struct {
    27. model string
    28. }
    29. // NewIPhone 苹果手机创建方法
    30. func NewIPhone(model string) *IPhone {
    31. return &IPhone{
    32. model: model,
    33. }
    34. }
    35. // ConnectLightning 苹果手机Lightning充电插槽
    36. func (i *IPhone) ConnectLightning() string {
    37. return fmt.Sprintf("%v connect lightning plug", i.model)
    38. }
    • 充电宝适配器

    1. package adapter
    2. import "fmt"
    3. // CommonPlug 通用的USB电源插槽
    4. type CommonPlug interface {
    5. ConnectUSB() string
    6. }
    7. // HuaweiPhonePlugAdapter 华为TypeC充电插槽适配通用USB充电插槽
    8. type HuaweiPhonePlugAdapter struct {
    9. huaweiPhone HuaweiPlug
    10. }
    11. // NewHuaweiPhonePlugAdapter 创建华为手机适配USB充电插槽适配器
    12. func NewHuaweiPhonePlugAdapter(huaweiPhone HuaweiPlug) *HuaweiPhonePlugAdapter {
    13. return &HuaweiPhonePlugAdapter{
    14. huaweiPhone: huaweiPhone,
    15. }
    16. }
    17. // ConnectUSB 链接USB
    18. func (h *HuaweiPhonePlugAdapter) ConnectUSB() string {
    19. return fmt.Sprintf("%v adapt to usb ", h.huaweiPhone.ConnectTypeC())
    20. }
    21. // ApplePhonePlugAdapter 苹果Lightning充电插槽适配通用USB充电插槽
    22. type ApplePhonePlugAdapter struct {
    23. iPhone ApplePlug
    24. }
    25. // NewApplePhonePlugAdapter 创建苹果手机适配USB充电插槽适配器
    26. func NewApplePhonePlugAdapter(iPhone ApplePlug) *ApplePhonePlugAdapter {
    27. return &ApplePhonePlugAdapter{
    28. iPhone: iPhone,
    29. }
    30. }
    31. // ConnectUSB 链接USB
    32. func (a *ApplePhonePlugAdapter) ConnectUSB() string {
    33. return fmt.Sprintf("%v adapt to usb ", a.iPhone.ConnectLightning())
    34. }
    35. // PowerBank 充电宝
    36. type PowerBank struct {
    37. brand string
    38. }
    39. // Charge 支持通用USB接口充电
    40. func (p *PowerBank) Charge(plug CommonPlug) string {
    41. return fmt.Sprintf("%v power bank connect usb plug, start charge for %v", p.brand, plug.ConnectUSB())
    42. }
    • 测试程序

    1. package adapter
    2. import (
    3. "fmt"
    4. "testing"
    5. )
    6. func TestAdapter (t *testing.T) {
    7. huaweiMate40Pro := NewHuaweiPhone("华为 mate40 pro")
    8. iphone13MaxPro := NewIPhone("苹果 iphone13 pro max")
    9. powerBank := &PowerBank{"飞利浦"}
    10. fmt.Println(powerBank.Charge(NewHuaweiPhonePlugAdapter(huaweiMate40Pro)))
    11. fmt.Println(powerBank.Charge(NewApplePhonePlugAdapter(iphone13MaxPro)))
    12. }
    • 运行结果

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

    b3fb24379377b7dcebd8b86654d343e4.jpeg

    桥接模式

    (一)概念

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

    层次结构中的第一层(通常称为抽象部分)将包含对第二层 (实现部分) 对象的引用。抽象部分将能将一些(有时是绝大部分)对自己的调用委派给实现部分的对象。所有的实现部分都有一个通用接口,因此它们能在抽象部分内部相互替换。

    简单的说,一个事物存在多个维度的变化点,每一个维度都抽象出一个接口,事物引用这些接口实现整体行为逻辑,而每一个接口都可以存在多个变化的实现。

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

    (二)示例

    对于一段经历的描述,经历就可能有多种实现,比如旅游经历,探险经历这相当于第一层次的类结构,同时描述旅游经历或探险经历又包含多个维度,比如如何到达目的地,在目的地开展了什么活动等,到达目的地有很多种方式,比如飞机、火车、汽车等;开展的活动又根据地点不同而不同,海边可以冲浪,山地可以攀岩,荒漠可以徒步穿越等;这两个维度的变化点对于描述经历来说相当于第二层次类实现,通过接口被第一层次引用。

    这里对于经历描述存在三个维度的变化,

    1.经历本身的两个实现:旅游经历与探险经历。

    2.交通方式的两个实现:飞机和汽车。

    3.开展活动的三个实现:冲浪、攀岩与徒步穿越。

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

    • 交通工具

    1. package bridge
    2. // Traffic 交通工具
    3. type Traffic interface {
    4. Transport() string
    5. }
    6. // airplane 飞机
    7. type airplane struct{}
    8. // Transport 坐飞机
    9. func (a *airplane) Transport() string {
    10. return "by airplane"
    11. }
    12. // car 汽车
    13. type car struct{}
    14. // Transport 坐汽车
    15. func (t *car) Transport() string {
    16. return "by car"
    17. }
    • 目的地

    1. package bridge
    2. import "fmt"
    3. // Location 地点
    4. type Location interface {
    5. Name() string // 地点名称
    6. PlaySports() string // 参与运动
    7. }
    8. // namedLocation 被命名的地点,统一引用此类型,声明名字字段及获取方法
    9. type namedLocation struct {
    10. name string
    11. }
    12. // Name 获取地点名称
    13. func (n namedLocation) Name() string {
    14. return n.name
    15. }
    16. // seaside 海边
    17. type seaside struct {
    18. namedLocation
    19. }
    20. // NewSeaside 创建指定名字的海边,比如三亚湾
    21. func NewSeaside(name string) *seaside {
    22. return &seaside{
    23. namedLocation: namedLocation{
    24. name: name,
    25. },
    26. }
    27. }
    28. // PlaySports 海边可以冲浪
    29. func (s *seaside) PlaySports() string {
    30. return fmt.Sprintf("surfing")
    31. }
    32. // mountain 山
    33. type mountain struct {
    34. namedLocation
    35. }
    36. // NewMountain 创建指定名字的山,比如泰山
    37. func NewMountain(name string) *mountain {
    38. return &mountain{
    39. namedLocation: namedLocation{
    40. name: name,
    41. },
    42. }
    43. }
    44. // PlaySports 可以爬山
    45. func (m *mountain) PlaySports() string {
    46. return fmt.Sprintf("climbing")
    47. }
    48. // desert 荒漠
    49. type desert struct {
    50. namedLocation
    51. }
    52. // NewDesert 创建指定名字的荒漠,比如罗布泊
    53. func NewDesert(name string) *desert {
    54. return &desert{
    55. namedLocation: namedLocation{
    56. name: name,
    57. },
    58. }
    59. }
    60. // PlaySports 荒漠可以徒步穿越
    61. func (d *desert) PlaySports() string {
    62. return fmt.Sprintf("trekking")
    63. }
    • 经历描述

    1. package bridge
    2. import "fmt"
    3. // Experience 经历
    4. type Experience interface {
    5. Describe() string // 描述经历
    6. }
    7. // travelExperience 旅游经历
    8. type travelExperience struct {
    9. subject string
    10. traffic Traffic
    11. location Location
    12. }
    13. // NewTravelExperience 创建旅游经历,包括主题、交通方式、地点
    14. func NewTravelExperience(subject string, traffic Traffic, location Location) *travelExperience {
    15. return &travelExperience{
    16. subject: subject,
    17. traffic: traffic,
    18. location: location,
    19. }
    20. }
    21. // Describe 描述旅游经历
    22. func (t *travelExperience) Describe() string {
    23. return fmt.Sprintf("%s is to %s %s and %s", t.subject, t.location.Name(), t.traffic.Transport(), t.location.PlaySports())
    24. }
    25. // adventureExperience 探险经历
    26. type adventureExperience struct {
    27. survivalTraining string
    28. travelExperience
    29. }
    30. // NewAdventureExperience 创建探险经历,包括探险需要的培训,其他的与路由参数类似
    31. func NewAdventureExperience(training string, subject string, traffic Traffic, location Location) *adventureExperience {
    32. return &adventureExperience{
    33. survivalTraining: training,
    34. travelExperience: *NewTravelExperience(subject, traffic, location),
    35. }
    36. }
    37. // Describe 描述探险经历
    38. func (a *adventureExperience) Describe() string {
    39. return fmt.Sprintf("after %s, %s", a.survivalTraining, a.travelExperience.Describe())
    40. }
    • 测试程序

    1. package bridge
    2. import (
    3. "fmt"
    4. "testing"
    5. )
    6. func TestBridge(t *testing.T) {
    7. // 坐飞机去三亚度蜜月
    8. honeymoonTravel := NewTravelExperience("honeymoon", new(airplane), NewSeaside("SanyaYalongBay"))
    9. fmt.Println(honeymoonTravel.Describe())
    10. // 坐车去泰山毕业旅游
    11. graduationTrip := NewTravelExperience("graduationTrip", new(car), NewMountain("Tarzan"))
    12. fmt.Println(graduationTrip.Describe())
    13. // 野外生存培训后,坐车去罗布泊,徒步穿越
    14. desertAdventure := NewAdventureExperience("wilderness survival training", "adventure", new(car), NewDesert("Lop Nor"))
    15. fmt.Println(desertAdventure.Describe())
    16. }
    • 运行结果

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

    a02de36b719fcc3a2912f82bce79b3cf.jpeg

    组合模式

    (一)概念

    组合是一种结构型设计模式,你可以使用它将对象组合成树状结构,并且能像使用独立对象一样使用它们。

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

    (二)示例

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

    • 组件接口

    1. package composite
    2. // Region 行政区,作为组合模式component接口
    3. type Region interface {
    4. Name() string // 名称
    5. Population() int //人口
    6. GDP() float64 // gdp
    7. }
    • 区镇实现

    1. package composite
    2. // town 区镇,组合模式中相当于叶子节点
    3. type town struct {
    4. name string
    5. population int
    6. gdp float64
    7. }
    8. // NewTown 创建区镇,根据名称、人口、GDP
    9. func NewTown(name string, population int, gdp float64) *town {
    10. return &town{
    11. name: name,
    12. population: population,
    13. gdp: gdp,
    14. }
    15. }
    16. func (c *town) Name() string {
    17. return c.name
    18. }
    19. func (c *town) Population() int {
    20. return c.population
    21. }
    22. func (c *town) GDP() float64 {
    23. return c.gdp
    24. }
    • 县市地市实现

    1. package composite
    2. // cities 市,包括县市或者地市,组合模式中相当于composite
    3. type cities struct {
    4. name string
    5. regions map[string]Region
    6. }
    7. // NewCities 创建一个市
    8. func NewCities(name string) *cities {
    9. return &cities{
    10. name: name,
    11. regions: make(map[string]Region),
    12. }
    13. }
    14. func (c *cities) Name() string {
    15. return c.name
    16. }
    17. func (c *cities) Population() int {
    18. sum := 0
    19. for _, r := range c.regions {
    20. sum += r.Population()
    21. }
    22. return sum
    23. }
    24. func (c *cities) GDP() float64 {
    25. sum := 0.0
    26. for _, r := range c.regions {
    27. sum += r.GDP()
    28. }
    29. return sum
    30. }
    31. // Add 添加多个行政区
    32. func (c *cities) Add(regions ...Region) {
    33. for _, r := range regions {
    34. c.regions[r.Name()] = r
    35. }
    36. }
    37. // Remove 递归删除行政区
    38. func (c *cities) Remove(name string) {
    39. for n, r := range c.regions {
    40. if n == name {
    41. delete(c.regions, name)
    42. return
    43. }
    44. if city, ok := r.(*cities); ok {
    45. city.Remove(name)
    46. }
    47. }
    48. }
    49. func (c *cities) Regions() map[string]Region {
    50. return c.regions
    51. }
    • 测试程序

    1. package composite
    2. import (
    3. "fmt"
    4. "testing"
    5. )
    6. func TestComposite(t *testing.T) {
    7. gusu := NewTown("姑苏区", 100, 2000.00)
    8. fmt.Println(ShowRegionInfo(gusu))
    9. wuzhong := NewTown("吴中区", 150, 2600.00)
    10. fmt.Println(ShowRegionInfo(wuzhong))
    11. huqiu := NewTown("虎丘区", 80, 1800.00)
    12. fmt.Println(ShowRegionInfo(huqiu))
    13. kunshan := NewCities("昆山市")
    14. kunshan.Add(NewTown("玉山镇", 60, 1200.00),
    15. NewTown("周庄镇", 68, 1900.00),
    16. NewTown("花桥镇", 78, 2200.00))
    17. fmt.Println(ShowRegionInfo(kunshan))
    18. changshu := NewCities("常熟市")
    19. changshu.Add(NewTown("沙家浜镇", 55, 1100.00),
    20. NewTown("古里镇", 59, 1300.00),
    21. NewTown("辛庄镇", 68, 2100.00))
    22. fmt.Println(ShowRegionInfo(changshu))
    23. suzhou := NewCities("苏州市")
    24. suzhou.Add(gusu, wuzhong, huqiu, kunshan, changshu)
    25. fmt.Println(ShowRegionInfo(suzhou))
    26. }
    27. func ShowRegionInfo(region Region) string {
    28. return fmt.Sprintf("%s, 人口:%d万, GDP:%.2f亿", region.Name(), region.Population(), region.GDP())
    29. }
    • 运行结果

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

    d56dd9f4658fdeed21ecfa46a77cf14c.jpeg

    装饰模式

    (一)概念

    装饰是一种结构设计模式,允许你通过将对象放入特殊封装对象中来为原对象增加新的行为。

    由于目标对象和装饰器遵循同一接口,因此你可用装饰来对对象进行无限次的封装。结果对象将获得所有封装器叠加而来的行为。

    (二)示例

    地铁进站的过程一般情况下只需要买票,检票进站,但是如果你是带行李,就需要进行安全检查,如果是疫情时期,就需要进行疫情防护检查,比如戴口罩、测量体温等,这里买票进站相当于通用进站流程,安检及防疫检查就相当于加强的修饰行为。

    • 修饰器实现

    1. package decorator
    2. import "fmt"
    3. // Station 车站,修饰器模式统一接口
    4. type Station interface {
    5. Enter() string // 进站
    6. }
    7. // subwayStation 地铁站
    8. type subwayStation struct {
    9. name string
    10. }
    11. // NewSubwayStation 创建指定站名地铁站
    12. func NewSubwayStation(name string) *subwayStation {
    13. return &subwayStation{
    14. name: name,
    15. }
    16. }
    17. // Enter 进地铁站
    18. func (s *subwayStation) Enter() string {
    19. return fmt.Sprintf("买票进入%s地铁站。", s.name)
    20. }
    21. // securityCheckDecorator 进站安检修饰器
    22. type securityCheckDecorator struct {
    23. station Station
    24. }
    25. func NewSecurityCheckDecorator(station Station) *securityCheckDecorator {
    26. return &securityCheckDecorator{
    27. station: station,
    28. }
    29. }
    30. func (s *securityCheckDecorator) Enter() string {
    31. return "行李通过安检;" + s.station.Enter()
    32. }
    33. // epidemicProtectionDecorator 进站疫情防护修饰器
    34. type epidemicProtectionDecorator struct {
    35. station Station
    36. }
    37. func NewEpidemicProtectionDecorator(station Station) *epidemicProtectionDecorator {
    38. return &epidemicProtectionDecorator{
    39. station: station,
    40. }
    41. }
    42. func (e *epidemicProtectionDecorator) Enter() string {
    43. return "测量体温,佩戴口罩;" + e.station.Enter()
    44. }
    • 测试代码

    1. package decorator
    2. import (
    3. "fmt"
    4. "testing"
    5. )
    6. func TestDecorator(t *testing.T) {
    7. xierqiStation := NewSubwayStation("西二旗")
    8. fmt.Println(EnhanceEnterStationProcess(xierqiStation, false, false).Enter())
    9. fmt.Println(EnhanceEnterStationProcess(xierqiStation, true, false).Enter())
    10. fmt.Println(EnhanceEnterStationProcess(xierqiStation, true, true).Enter())
    11. }
    12. // EnhanceEnterStationProcess 根据是否有行李,是否处于疫情,增加进站流程
    13. func EnhanceEnterStationProcess(station Station, hasLuggage bool, hasEpidemic bool) Station {
    14. if hasLuggage {
    15. station = NewSecurityCheckDecorator(station)
    16. }
    17. if hasEpidemic {
    18. station = NewEpidemicProtectionDecorator(station)
    19. }
    20. return station
    21. }
    • 运行结果

    1. === RUN TestDecorator
    2. 买票进入西二旗地铁站。
    3. 行李通过安检;买票进入西二旗地铁站。
    4. 测量体温,佩戴口罩;行李通过安检;买票进入西二旗地铁站。
    5. --- PASS: TestDecorator (0.00s)
    6. PASS

    50066bc61218cf9659dd23d3d1371cea.jpeg

    外观模式

    (一)概念

    外观是一种结构型设计模式,能为复杂系统、程序库或框架提供一个简单 (但有限) 的接口。

    尽管外观模式降低了程序的整体复杂度,但它同时也有助于将不需要的依赖移动到同一个位置。

    (二)示例

    用户在淘宝电商系统买商品时,只需要选好商品在结算页点击提交即可完成下单;在客户端系统仅需要一个创建订单的方法,但是整个订单的生成需要很多步骤,比如查询用户配送地址,查询商品价格,使用优惠券,扣减商品库存,支付相应价钱等。

    • 淘宝电商系统

    1. package facade
    2. import "fmt"
    3. // TaobaoFacade 淘宝网站门面,在淘宝网站下单涉及到多个系统配合调用,包括用户系统,商品系统,优惠券系统,库存系统,支付系统,最终生成订单
    4. type TaobaoFacade struct {
    5. userService *UserService
    6. productService *ProductService
    7. couponService *CouponService
    8. stockService *StockService
    9. paymentService *PaymentService
    10. }
    11. // NewTaobaoFacade 创建淘宝网站
    12. func NewTaobaoFacade() *TaobaoFacade {
    13. return &TaobaoFacade{
    14. userService: &UserService{},
    15. productService: &ProductService{
    16. products: map[string]float64{"笔记本电脑": 6666.66},
    17. },
    18. couponService: &CouponService{},
    19. stockService: &StockService{},
    20. paymentService: &PaymentService{},
    21. }
    22. }
    23. // CreateOrder 根据用户名,商品名,商品数量生成购买订单
    24. func (t *TaobaoFacade) CreateOrder(userName string, productName string, count int) string {
    25. // 使用优惠券
    26. couponInfo := t.couponService.useCoupon()
    27. // 扣减库存
    28. stockInfo := t.stockService.decreaseFor(productName, count)
    29. // 计算商品总价
    30. sumPrice := t.productService.getProductPrice(productName) * float64(count)
    31. // 支付价格
    32. payInfo := t.paymentService.pay(sumPrice)
    33. return fmt.Sprintf("用户%s,购买了%d件%s商品,%s,%s,%s,送货到%s", userName, count, productName, couponInfo,
    34. stockInfo, payInfo, t.userService.getUserAddress(userName))
    35. }
    36. // UserService 用户系统
    37. type UserService struct{}
    38. func (u *UserService) getUserAddress(userName string) string {
    39. return fmt.Sprintf("%s地址是:北京市海淀区中关村大街,1号院2号楼3单元402", userName)
    40. }
    41. // ProductService 商品系统
    42. type ProductService struct {
    43. products map[string]float64
    44. }
    45. func (p *ProductService) getProductPrice(productName string) float64 {
    46. return p.products[productName]
    47. }
    48. // CouponService 优惠券系统
    49. type CouponService struct{}
    50. func (c *CouponService) useCoupon() string {
    51. return "使用满100减20优惠券"
    52. }
    53. // StockService 库存系统
    54. type StockService struct{}
    55. func (s *StockService) decreaseFor(productName string, count int) string {
    56. return fmt.Sprintf("扣减%d件%s商品库存", count, productName)
    57. }
    58. // PaymentService 支付系统
    59. type PaymentService struct{}
    60. func (p *PaymentService) pay(amount float64) string {
    61. return fmt.Sprintf("支付金额%.2f", amount)
    62. }
    • 测试程序

    1. package facade
    2. import (
    3. "fmt"
    4. "testing"
    5. )
    6. func TestFacade(t *testing.T) {
    7. // 通过门面模式,隐藏下单过程中,后端多个系统的复杂交互
    8. taobao := NewTaobaoFacade()
    9. fmt.Println(taobao.CreateOrder("张三", "笔记本电脑", 1))
    10. }
    • 运行结果

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

    30495498c76e7ae96e73ac334dd24bfa.jpeg

    享元模式

    (一)概念

    享元是一种结构型设计模式,它允许你在消耗少量内存的情况下支持大量对象。

    模式通过共享多个对象的部分状态来实现上述功能。换句话来说,享元会将不同对象的相同数据进行缓存以节省内存。

    (二)示例

    北京出租车调度系统,需要每隔一分钟记录一下全市出租车的位置信息,假设为了提高系统响应速度,近一天的数据需要存储在内存中,每个位置信息包括出租车辆信息及位置信息,位置信息在系统中就是一个(x,y)坐标,车辆信息包括车的号牌,颜色,品牌和所属公司,在调度系统存储的出租车行驶轨迹中,位置是实时在变化的,但车辆信息就可以通过享元模式共用一个对象引用,来减少内存消耗。

    • 出租车享元对象

    1. package flyweight
    2. import (
    3. "fmt"
    4. )
    5. // Taxi 出租车,享元对象,保存不变的内在属性信息
    6. type Taxi struct {
    7. licensePlate string // 车牌
    8. color string // 颜色
    9. brand string // 汽车品牌
    10. company string // 所属公司
    11. }
    12. // LocateFor 获取定位信息
    13. func (t *Taxi) LocateFor(monitorMap string, x, y int) string {
    14. return fmt.Sprintf("%s,对于车牌号%s,%s,%s品牌,所属%s公司,定位(%d,%d)", monitorMap,
    15. t.licensePlate, t.color, t.brand, t.company, x, y)
    16. }
    17. // taxiFactoryInstance 出租车工厂单例
    18. var taxiFactoryInstance = &TaxiFactory{
    19. taxis: make(map[string]*Taxi),
    20. }
    21. // GetTaxiFactory 获取出租车工厂单例
    22. func GetTaxiFactory() *TaxiFactory {
    23. return taxiFactoryInstance
    24. }
    25. // TaxiFactory 出租车工厂类
    26. type TaxiFactory struct {
    27. taxis map[string]*Taxi // key为车牌号
    28. }
    29. // getTaxi 获取出租车
    30. func (f *TaxiFactory) getTaxi(licensePlate, color, brand, company string) *Taxi {
    31. if _, ok := f.taxis[licensePlate]; !ok {
    32. f.taxis[licensePlate] = &Taxi{
    33. licensePlate: licensePlate,
    34. color: color,
    35. brand: brand,
    36. company: company,
    37. }
    38. }
    39. return f.taxis[licensePlate]
    40. }
    • 出租车调度系统

    1. package flyweight
    2. import "bytes"
    3. // TaxiPosition 出租车位置信息 x,y为外在数据信息,taxi为内在数据信息(享元对象)
    4. type TaxiPosition struct {
    5. x int
    6. y int
    7. taxi *Taxi
    8. }
    9. func NewTaxiPosition(taxi *Taxi, x, y int) *TaxiPosition {
    10. return &TaxiPosition{
    11. taxi: taxi,
    12. x: x,
    13. y: y,
    14. }
    15. }
    16. // LocateFor 定位信息
    17. func (p *TaxiPosition) LocateFor(monitorMap string) string {
    18. return p.taxi.LocateFor(monitorMap, p.x, p.y)
    19. }
    20. // TaxiDispatcher 出租车调度系统
    21. type TaxiDispatcher struct {
    22. name string
    23. traces map[string][]*TaxiPosition // 存储出租车当天轨迹信息,key为车牌号
    24. }
    25. func NewTaxiDispatcher(name string) *TaxiDispatcher {
    26. return &TaxiDispatcher{
    27. name: name,
    28. traces: make(map[string][]*TaxiPosition),
    29. }
    30. }
    31. // AddTrace 添加轨迹
    32. func (t *TaxiDispatcher) AddTrace(licensePlate, color, brand, company string, x, y int) {
    33. taxi := GetTaxiFactory().getTaxi(licensePlate, color, brand, company)
    34. t.traces[licensePlate] = append(t.traces[licensePlate], NewTaxiPosition(taxi, x, y))
    35. }
    36. // ShowTraces 显示轨迹
    37. func (t *TaxiDispatcher) ShowTraces(licensePlate string) string {
    38. bytesBuf := bytes.Buffer{}
    39. for _, trace := range t.traces[licensePlate] {
    40. bytesBuf.WriteString(trace.LocateFor(t.name))
    41. bytesBuf.WriteByte('\n')
    42. }
    43. return bytesBuf.String()
    44. }
    • 测试程序

    1. package flyweight
    2. import (
    3. "fmt"
    4. "testing"
    5. )
    6. func TestFlyweight(t *testing.T) {
    7. dispatcher := NewTaxiDispatcher("北京市出租车调度系统")
    8. dispatcher.AddTrace("京B.123456", "黄色", "北京现代", "北汽", 10, 20)
    9. dispatcher.AddTrace("京B.123456", "黄色", "北京现代", "北汽", 20, 30)
    10. dispatcher.AddTrace("京B.123456", "黄色", "北京现代", "北汽", 30, 40)
    11. dispatcher.AddTrace("京B.123456", "黄色", "北京现代", "北汽", 40, 50)
    12. dispatcher.AddTrace("京B.567890", "红色", "一汽大众", "首汽", 20, 40)
    13. dispatcher.AddTrace("京B.567890", "红色", "一汽大众", "首汽", 50, 50)
    14. fmt.Println(dispatcher.ShowTraces("京B.123456"))
    15. fmt.Println(dispatcher.ShowTraces("京B.567890"))
    16. }
    • 运行结果

    1. === RUN TestFlyweight
    2. 北京市出租车调度系统,对于车牌号京B.123456,黄色,北京现代品牌,所属北汽公司,定位(10,20)
    3. 北京市出租车调度系统,对于车牌号京B.123456,黄色,北京现代品牌,所属北汽公司,定位(20,30)
    4. 北京市出租车调度系统,对于车牌号京B.123456,黄色,北京现代品牌,所属北汽公司,定位(30,40)
    5. 北京市出租车调度系统,对于车牌号京B.123456,黄色,北京现代品牌,所属北汽公司,定位(40,50)
    6. 北京市出租车调度系统,对于车牌号京B.567890,红色,一汽大众品牌,所属首汽公司,定位(20,40)
    7. 北京市出租车调度系统,对于车牌号京B.567890,红色,一汽大众品牌,所属首汽公司,定位(50,50)
    8. --- PASS: TestFlyweight (0.00s)
    9. PASS

    f44d8a66cf6f0eca3cfd5cab379b71aa.jpeg

    代理模式

    (一)概念

    代理是一种结构型设计模式,让你能提供真实服务对象的替代品给客户端使用。代理接收客户端的请求并进行一些处理 (访问控制和缓存等), 然后再将请求传递给服务对象。

    代理对象拥有和服务对象相同的接口,这使得当其被传递给客户端时可与真实对象互换。

    修饰与代理是非常相似的设计模式,都是基于组合设计原则,也就是说一个对象应该将部分工作委派给另一个对象。但两者之间不同点我认为是,修饰器模式总是要执行服务对象,对于执行之前或执行之后结果进行加强,服务对象基本是客户端创建好再嵌套外层的修饰对象;而代理模式不一定执行服务对象,有可能通过缓存,延迟加载等没有访问服务对象,同时服务对象什么时候创建也是由代理类决定的。

    (二)示例

    房屋中介代理帮助房东卖房子,这个过程就是一个代理模式的过程,中介会收集尽量多的卖房信息,并通过各种渠道发布,同时中介会随时带客户看房,并初步商讨价格,如果达成初步购买意向,才会约房东讨论房屋价格,最后签约卖房;房屋中介与房东都实现卖房接口,中介会提前坐一些前期工作,如果都没问题,才会约房东执行真正的签约卖房流程。

    • 房屋中介卖房

    1. package proxy
    2. import (
    3. "bytes"
    4. "fmt"
    5. )
    6. // HouseSeller 房屋出售者
    7. type HouseSeller interface {
    8. SellHouse(address string, buyer string) string
    9. }
    10. // houseProxy 房产中介代理
    11. type houseProxy struct {
    12. houseSeller HouseSeller
    13. }
    14. func NewHouseProxy(houseSeller HouseSeller) *houseProxy {
    15. return &houseProxy{
    16. houseSeller: houseSeller,
    17. }
    18. }
    19. // SellHouse 中介卖房,看房->初步谈价->最终和房东签协议
    20. func (h *houseProxy) SellHouse(address string, buyer string) string {
    21. buf := bytes.Buffer{}
    22. buf.WriteString(h.viewHouse(address, buyer) + "\n")
    23. buf.WriteString(h.preBargain(address, buyer) + "\n")
    24. buf.WriteString(h.houseSeller.SellHouse(address, buyer))
    25. return buf.String()
    26. }
    27. // viewHouse 看房介绍基本情况
    28. func (h *houseProxy) viewHouse(address string, buyer string) string {
    29. return fmt.Sprintf("带买家%s看位于%s的房屋,并介绍基本情况", buyer, address)
    30. }
    31. // preBargain 初步沟通价格
    32. func (h *houseProxy) preBargain(address string, buyer string) string {
    33. return fmt.Sprintf("讨价还价后,初步达成购买意向")
    34. }
    35. // houseOwner 房东
    36. type houseOwner struct{}
    37. // SellHouse 房东卖房,商讨价格,签署购房协议
    38. func (h *houseOwner) SellHouse(address string, buyer string) string {
    39. return fmt.Sprintf("最终商讨价格后,与%s签署购买地址为%s的购房协议。", buyer, address)
    40. }
    • 测试程序

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

    1. === RUN TestProxy
    2. 带买家李四看位于北京市海淀区中关村大街,2号院1号楼4单元502室的房屋,并介绍基本情况
    3. 讨价还价后,初步达成购买意向
    4. 最终商讨价格后,与李四签署购买地址为北京市海淀区中关村大街,2号院1号楼4单元502室的购房协议。
    5. --- PASS: TestProxy (0.00s)
    6. PASS

    90f64c37b19118f36cc63ff4291645e2.jpeg

    工厂方法模式

    (一)概念

    工厂方法模式是一种创建型设计模式,其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。

    (二)示例

    摊煎饼的小贩需要先摊个煎饼,再卖出去,摊煎饼就可以类比为一个工厂方法,根据顾客的喜好摊出不同口味的煎饼。

    • 接口

    1. package factorymethod
    2. // Pancake 煎饼
    3. type Pancake interface {
    4. // ShowFlour 煎饼使用的面粉
    5. ShowFlour() string
    6. // Value 煎饼价格
    7. Value() float32
    8. }
    9. // PancakeCook 煎饼厨师
    10. type PancakeCook interface {
    11. // MakePancake 摊煎饼
    12. MakePancake() Pancake
    13. }
    14. // PancakeVendor 煎饼小贩
    15. type PancakeVendor struct {
    16. PancakeCook
    17. }
    18. // NewPancakeVendor ...
    19. func NewPancakeVendor(cook PancakeCook) *PancakeVendor {
    20. return &PancakeVendor{
    21. PancakeCook: cook,
    22. }
    23. }
    24. // SellPancake 卖煎饼,先摊煎饼,再卖
    25. func (vendor *PancakeVendor) SellPancake() (money float32) {
    26. return vendor.MakePancake().Value()
    27. }
    • 实现

    各种面的煎饼实现

    1. package factorymethod
    2. // cornPancake 玉米面煎饼
    3. type cornPancake struct{}
    4. // NewCornPancake ...
    5. func NewCornPancake() *cornPancake {
    6. return &cornPancake{}
    7. }
    8. func (cake *cornPancake) ShowFlour() string {
    9. return "玉米面"
    10. }
    11. func (cake *cornPancake) Value() float32 {
    12. return 5.0
    13. }
    14. // milletPancake 小米面煎饼
    15. type milletPancake struct{}
    16. func NewMilletPancake() *milletPancake {
    17. return &milletPancake{}
    18. }
    19. func (cake *milletPancake) ShowFlour() string {
    20. return "小米面"
    21. }
    22. func (cake *milletPancake) Value() float32 {
    23. return 8.0
    24. }

    制作各种口味煎饼的工厂方法实现

    1. package factorymethod
    2. // cornPancakeCook 制作玉米面煎饼厨师
    3. type cornPancakeCook struct{}
    4. func NewCornPancakeCook() *cornPancakeCook {
    5. return &cornPancakeCook{}
    6. }
    7. func (cook *cornPancakeCook) MakePancake() Pancake {
    8. return NewCornPancake()
    9. }
    10. // milletPancakeCook 制作小米面煎饼厨师
    11. type milletPancakeCook struct{}
    12. func NewMilletPancakeCook() *milletPancakeCook {
    13. return &milletPancakeCook{}
    14. }
    15. func (cook *milletPancakeCook) MakePancake() Pancake {
    16. return NewMilletPancake()
    17. }
    • 运用

    1. package factorymethod
    2. import (
    3. "fmt"
    4. "testing"
    5. )
    6. func TestFactoryMethod(t *testing.T) {
    7. pancakeVendor := NewPancakeVendor(NewCornPancakeCook())
    8. fmt.Printf("Corn pancake value is %v\n", pancakeVendor.SellPancake())
    9. pancakeVendor = NewPancakeVendor(NewMilletPancakeCook())
    10. fmt.Printf("Millet pancake value is %v\n", pancakeVendor.SellPancake())
    11. }

    输出:

    1. === RUN TestFactoryMethod
    2. Corn pancake value is 5
    3. Millet pancake value is 8
    4. --- PASS: TestFactoryMethod (0.00s)
    5. PASS

    c337e765d63ed78d8533ca8349ce57ee.png

    抽象工厂模式

    (一)概念

    抽象工厂是一种创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类。

    抽象工厂定义了用于创建不同产品的接口,但将实际的创建工作留给了具体工厂类。每个工厂类型都对应一个特定的产品变体。

    在创建产品时,客户端代码调用的是工厂对象的构建方法,而不是直接调用构造函数 ( new操作符)。由于一个工厂对应一种产品变体,因此它创建的所有产品都可相互兼容。

    客户端代码仅通过其抽象接口与工厂和产品进行交互。该接口允许同一客户端代码与不同产品进行交互。你只需创建一个具体工厂类并将其传递给客户端代码即可。

    (二)示例

    厨师准备一餐时,会分别做吃的和喝的,根据早、中、晚三餐饮食习惯,会分别制作不同的饮食,厨师就相当于抽象工厂,制作三餐的不同烹饪方式就好比不同抽象工厂的实现。

    • 接口

    1. package abstractfactory
    2. // Cook 厨师接口,抽象工厂
    3. type Cook interface {
    4. // MakeFood 制作主食
    5. MakeFood() Food
    6. // MakeDrink 制作饮品
    7. MakeDrink() Drink
    8. }
    9. // Food 主食接口
    10. type Food interface {
    11. // Eaten 被吃
    12. Eaten() string
    13. }
    14. // Drink 饮品接口
    15. type Drink interface {
    16. // Drunk 被喝
    17. Drunk() string
    18. }
    • 实现

    三餐不同厨师接口的实现

    1. package abstractfactory
    2. // breakfastCook 早餐厨师
    3. type breakfastCook struct{}
    4. func NewBreakfastCook() *breakfastCook {
    5. return &breakfastCook{}
    6. }
    7. func (b *breakfastCook) MakeFood() Food {
    8. return &cakeFood{"切片面包"}
    9. }
    10. func (b *breakfastCook) MakeDrink() Drink {
    11. return &gruelDrink{"小米粥"}
    12. }
    13. // lunchCook 午餐厨师
    14. type lunchCook struct{}
    15. func NewLunchCook() *lunchCook {
    16. return &lunchCook{}
    17. }
    18. func (l *lunchCook) MakeFood() Food {
    19. return &dishFood{"烤全羊"}
    20. }
    21. func (l *lunchCook) MakeDrink() Drink {
    22. return &sodaDrink{"冰镇可口可乐"}
    23. }
    24. // dinnerCook 晚餐厨师
    25. type dinnerCook struct{}
    26. func NewDinnerCook() *dinnerCook {
    27. return &dinnerCook{}
    28. }
    29. func (d *dinnerCook) MakeFood() Food {
    30. return &noodleFood{"大盘鸡拌面"}
    31. }
    32. func (d *dinnerCook) MakeDrink() Drink {
    33. return &soupDrink{"西红柿鸡蛋汤"}
    34. }

    不同吃的

    1. package abstractfactory
    2. import "fmt"
    3. // cakeFood 蛋糕
    4. type cakeFood struct {
    5. cakeName string
    6. }
    7. func (c *cakeFood) Eaten() string {
    8. return fmt.Sprintf("%v被吃", c.cakeName)
    9. }
    10. // dishFood 菜肴
    11. type dishFood struct {
    12. dishName string
    13. }
    14. func (d *dishFood) Eaten() string {
    15. return fmt.Sprintf("%v被吃", d.dishName)
    16. }
    17. // noodleFood 面条
    18. type noodleFood struct {
    19. noodleName string
    20. }
    21. func (n *noodleFood) Eaten() string {
    22. return fmt.Sprintf("%v被吃", n.noodleName)
    23. }

    不同喝的

    1. package abstractfactory
    2. import "fmt"
    3. // gruelDrink 粥
    4. type gruelDrink struct {
    5. gruelName string
    6. }
    7. func (g *gruelDrink) Drunk() string {
    8. return fmt.Sprintf("%v被喝", g.gruelName)
    9. }
    10. // sodaDrink 汽水
    11. type sodaDrink struct {
    12. sodaName string
    13. }
    14. func (s *sodaDrink) Drunk() string {
    15. return fmt.Sprintf("%v被喝", s.sodaName)
    16. }
    17. // soupDrink 汤
    18. type soupDrink struct {
    19. soupName string
    20. }
    21. func (s *soupDrink) Drunk() string {
    22. return fmt.Sprintf("%v被喝", s.soupName)
    23. }

    运用

    1. package abstractfactory
    2. import (
    3. "fmt"
    4. "testing"
    5. )
    6. func TestAbstractFactory(t *testing.T) {
    7. fmt.Printf("breakfast: %v\n", HaveMeal(NewBreakfastCook()))
    8. fmt.Printf("lunch: %v\n", HaveMeal(NewLunchCook()))
    9. fmt.Printf("dinner: %v\n", HaveMeal(NewDinnerCook()))
    10. }
    11. // HaveMeal 吃饭
    12. func HaveMeal(cook Cook) string {
    13. return fmt.Sprintf("%s %s", cook.MakeFood().Eaten(), cook.MakeDrink().Drunk())
    14. }

    输出

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

    863f8cc67005c4166212b677e662b7c5.png

    生成器模式

    (一)概念

    生成器是一种创建型设计模式,使你能够分步骤创建复杂对象。

    与其他创建型模式不同,生成器不要求产品拥有通用接口。这使得用相同的创建过程生成不同的产品成为可能。

    (二)示例

    还是摊煎饼的例子,摊煎饼分为四个步骤,1放面糊、2放鸡蛋、3放调料、4放薄脆,通过四个创建过程,制作好一个煎饼,这个摊煎饼的过程就好比煎饼生成器接口,不同生成器的实现就相当于摊不同品类的煎饼,比如正常的煎饼,健康的煎饼(可能用的是粗粮面、柴鸡蛋、非油炸薄脆、不放酱等),生成器接口方法也可以通过参数控制煎饼的大小,比如放两勺面糊,放2个鸡蛋等。

    生成器的使用者为了避免每次都调用相同的构建步骤,也可以通过包装类固定几种构建过程,生成几类常用的产品,就好像摊煎饼有几类常卖固定成品,比如普通的,加两个鸡蛋的,不要香菜的等等,这几类固定构建过程提前定制好,直接通过简单工厂方法就直接创建,如果用户再需要细粒度的定制构建,再通过生成器创建。

    • 接口

    1. package builder
    2. // Quantity 分量
    3. type Quantity int
    4. const (
    5. Small Quantity = 1
    6. Middle Quantity = 5
    7. Large Quantity = 10
    8. )
    9. type PancakeBuilder interface {
    10. // PutPaste 放面糊
    11. PutPaste(quantity Quantity)
    12. // PutEgg 放鸡蛋
    13. PutEgg(num int)
    14. // PutWafer 放薄脆
    15. PutWafer()
    16. // PutFlavour 放调料 Coriander香菜,Shallot葱 Sauce酱
    17. PutFlavour(hasCoriander, hasShallot, hasSauce bool)
    18. // Build 摊煎饼
    19. Build() *Pancake
    20. }
    21. // Pancake 煎饼
    22. type Pancake struct {
    23. pasteQuantity Quantity // 面糊分量
    24. eggNum int // 鸡蛋数量
    25. wafer string // 薄脆
    26. hasCoriander bool // 是否放香菜
    27. hasShallot bool // 是否放葱
    28. hasSauce bool // 是否放酱
    29. }
    • 实现

    正常煎饼创建器

    1. package builder
    2. type normalPancakeBuilder struct {
    3. pasteQuantity Quantity // 面糊量
    4. eggNum int // 鸡蛋数量
    5. friedWafer string // 油炸薄脆
    6. hasCoriander bool // 是否放香菜
    7. hasShallot bool // 是否放葱
    8. hasHotSauce bool // 是否放辣味酱
    9. }
    10. func NewNormalPancakeBuilder() *normalPancakeBuilder {
    11. return &normalPancakeBuilder{}
    12. }
    13. func (n *normalPancakeBuilder) PutPaste(quantity Quantity) {
    14. n.pasteQuantity = quantity
    15. }
    16. func (n *normalPancakeBuilder) PutEgg(num int) {
    17. n.eggNum = num
    18. }
    19. func (n *normalPancakeBuilder) PutWafer() {
    20. n.friedWafer = "油炸的薄脆"
    21. }
    22. func (n *normalPancakeBuilder) PutFlavour(hasCoriander, hasShallot, hasSauce bool) {
    23. n.hasCoriander = hasCoriander
    24. n.hasShallot = hasShallot
    25. n.hasHotSauce = hasSauce
    26. }
    27. func (n *normalPancakeBuilder) Build() *Pancake {
    28. return &Pancake{
    29. pasteQuantity: n.pasteQuantity,
    30. eggNum: n.eggNum,
    31. wafer: n.friedWafer,
    32. hasCoriander: n.hasCoriander,
    33. hasShallot: n.hasShallot,
    34. hasSauce: n.hasHotSauce,
    35. }
    36. }

    健康煎饼创建器

    1. package builder
    2. type healthyPancakeBuilder struct {
    3. milletPasteQuantity Quantity // 小米面糊量
    4. chaiEggNum int // 柴鸡蛋数量
    5. nonFriedWafer string // 非油炸薄脆
    6. hasCoriander bool // 是否放香菜
    7. hasShallot bool // 是否放葱
    8. }
    9. func NewHealthyPancakeBuilder() *healthyPancakeBuilder {
    10. return &healthyPancakeBuilder{}
    11. }
    12. func (n *healthyPancakeBuilder) PutPaste(quantity Quantity) {
    13. n.milletPasteQuantity = quantity
    14. }
    15. func (n *healthyPancakeBuilder) PutEgg(num int) {
    16. n.chaiEggNum = num
    17. }
    18. func (n *healthyPancakeBuilder) PutWafer() {
    19. n.nonFriedWafer = "非油炸的薄脆"
    20. }
    21. func (n *healthyPancakeBuilder) PutFlavour(hasCoriander, hasShallot, _ bool) {
    22. n.hasCoriander = hasCoriander
    23. n.hasShallot = hasShallot
    24. }
    25. func (n *healthyPancakeBuilder) Build() *Pancake {
    26. return &Pancake{
    27. pasteQuantity: n.milletPasteQuantity,
    28. eggNum: n.chaiEggNum,
    29. wafer: n.nonFriedWafer,
    30. hasCoriander: n.hasCoriander,
    31. hasShallot: n.hasShallot,
    32. hasSauce: false,
    33. }
    34. }

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

    1. package builder
    2. // PancakeCook 摊煎饼师傅
    3. type PancakeCook struct {
    4. builder PancakeBuilder
    5. }
    6. func NewPancakeCook(builder PancakeBuilder) *PancakeCook {
    7. return &PancakeCook{
    8. builder: builder,
    9. }
    10. }
    11. // SetPancakeBuilder 重新设置煎饼构造器
    12. func (p *PancakeCook) SetPancakeBuilder(builder PancakeBuilder) {
    13. p.builder = builder
    14. }
    15. // MakePancake 摊一个一般煎饼
    16. func (p *PancakeCook) MakePancake() *Pancake {
    17. p.builder.PutPaste(Middle)
    18. p.builder.PutEgg(1)
    19. p.builder.PutWafer()
    20. p.builder.PutFlavour(true, true, true)
    21. return p.builder.Build()
    22. }
    23. // MakeBigPancake 摊一个巨无霸煎饼
    24. func (p *PancakeCook) MakeBigPancake() *Pancake {
    25. p.builder.PutPaste(Large)
    26. p.builder.PutEgg(3)
    27. p.builder.PutWafer()
    28. p.builder.PutFlavour(true, true, true)
    29. return p.builder.Build()
    30. }
    31. // MakePancakeForFlavour 摊一个自选调料霸煎饼
    32. func (p *PancakeCook) MakePancakeForFlavour(hasCoriander, hasShallot, hasSauce bool) *Pancake {
    33. p.builder.PutPaste(Large)
    34. p.builder.PutEgg(3)
    35. p.builder.PutWafer()
    36. p.builder.PutFlavour(hasCoriander, hasShallot, hasSauce)
    37. return p.builder.Build()
    38. }
    • 运用

    1. package builder
    2. import (
    3. "fmt"
    4. "testing"
    5. )
    6. func TestBuilder(t *testing.T) {
    7. pancakeCook := NewPancakeCook(NewNormalPancakeBuilder())
    8. fmt.Printf("摊一个普通煎饼 %#v\n", pancakeCook.MakePancake())
    9. pancakeCook.SetPancakeBuilder(NewHealthyPancakeBuilder())
    10. fmt.Printf("摊一个健康的加量煎饼 %#v\n", pancakeCook.MakeBigPancake())
    11. }

    输出

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

    61e7e5660bc39e11b11e30346db97a25.png

    原型模式

    (一)概念

    原型是一种创建型设计模式,使你能够复制对象,甚至是复杂对象,而又无需使代码依赖它们所属的类。

    所有的原型类都必须有一个通用的接口, 使得即使在对象所属的具体类未知的情况下也能复制对象。原型对象可以生成自身的完整副本, 因为相同类的对象可以相互访问对方的私有成员变量。

    (二)示例

    纸质文件可以通过复印机轻松拷贝出多份,设置Paper接口,包含读取文件内容和克隆文件两个方法。同时声明两个类报纸(Newspaper)和简历(Resume)实现了Paper接口,通过复印机(Copier)复印出两类文件的副本,并读取文件副本内容。

    • 接口实现

    1. package prototype
    2. import (
    3. "bytes"
    4. "fmt"
    5. "io"
    6. )
    7. // Paper 纸张,包含读取内容的方法,拷贝纸张的方法,作为原型模式接口
    8. type Paper interface {
    9. io.Reader
    10. Clone() Paper
    11. }
    12. // Newspaper 报纸 实现原型接口
    13. type Newspaper struct {
    14. headline string
    15. content string
    16. }
    17. func NewNewspaper(headline string, content string) *Newspaper {
    18. return &Newspaper{
    19. headline: headline,
    20. content: content,
    21. }
    22. }
    23. func (np *Newspaper) Read(p []byte) (n int, err error) {
    24. buf := bytes.NewBufferString(fmt.Sprintf("headline:%s,content:%s", np.headline, np.content))
    25. return buf.Read(p)
    26. }
    27. func (np *Newspaper) Clone() Paper {
    28. return &Newspaper{
    29. headline: np.headline + "_copied",
    30. content: np.content,
    31. }
    32. }
    33. // Resume 简历 实现原型接口
    34. type Resume struct {
    35. name string
    36. age int
    37. experience string
    38. }
    39. func NewResume(name string, age int, experience string) *Resume {
    40. return &Resume{
    41. name: name,
    42. age: age,
    43. experience: experience,
    44. }
    45. }
    46. func (r *Resume) Read(p []byte) (n int, err error) {
    47. buf := bytes.NewBufferString(fmt.Sprintf("name:%s,age:%d,experience:%s", r.name, r.age, r.experience))
    48. return buf.Read(p)
    49. }
    50. func (r *Resume) Clone() Paper {
    51. return &Resume{
    52. name: r.name + "_copied",
    53. age: r.age,
    54. experience: r.experience,
    55. }
    56. }
    • 运用

    1. package prototype
    2. import (
    3. "fmt"
    4. "reflect"
    5. "testing"
    6. )
    7. func TestPrototype(t *testing.T) {
    8. copier := NewCopier("云打印机")
    9. oneNewspaper := NewNewspaper("Go是最好的编程语言", "Go语言十大优势")
    10. oneResume := NewResume("小明", 29, "5年码农")
    11. otherNewspaper := copier.copy(oneNewspaper)
    12. copyNewspaperMsg := make([]byte, 100)
    13. byteSize, _ := otherNewspaper.Read(copyNewspaperMsg)
    14. fmt.Println("copyNewspaperMsg:" + string(copyNewspaperMsg[:byteSize]))
    15. otherResume := copier.copy(oneResume)
    16. copyResumeMsg := make([]byte, 100)
    17. byteSize, _ = otherResume.Read(copyResumeMsg)
    18. fmt.Println("copyResumeMsg:" + string(copyResumeMsg[:byteSize]))
    19. }
    20. // Copier 复印机
    21. type Copier struct {
    22. name string
    23. }
    24. func NewCopier(n string) *Copier {
    25. return &Copier{name: n}
    26. }
    27. func (c *Copier) copy(paper Paper) Paper {
    28. fmt.Printf("copier name:%v is copying:%v ", c.name, reflect.TypeOf(paper).String())
    29. return paper.Clone()
    30. }

    输出

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

    110eb6014e805d7bd8f83b711213dfe1.png

    单例模式

    (一)概念

    单例是一种创建型设计模式,让你能够保证一个类只有一个实例,并提供一个访问该实例的全局节点。

    单例拥有与全局变量相同的优缺点。尽管它们非常有用,但却会破坏代码的模块化特性。

    (二)示例

    通过地球对象实现单例,earth不能导出,通过TheEarth方法访问全局唯一实例,并通过sync.Once实现多协程下一次加载。

    • 接口实现

    1. package singleton
    2. import "sync"
    3. var once sync.Once
    4. // 不可导出对象
    5. type earth struct {
    6. desc string
    7. }
    8. func (e *earth) String() string {
    9. return e.desc
    10. }
    11. // theEarth 地球单实例
    12. var theEarth *earth
    13. // TheEarth 获取地球单实例
    14. func TheEarth() *earth {
    15. if theEarth == nil {
    16. once.Do(func() {
    17. theEarth = &earth{
    18. desc: "美丽的地球,孕育了生命。",
    19. }
    20. })
    21. }
    22. return theEarth
    23. }
    • 运用

    1. package singleton
    2. import (
    3. "fmt"
    4. "testing"
    5. )
    6. func TestSingleton(t *testing.T) {
    7. fmt.Println(TheEarth().String())
    8. }

    输出

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

    点击下方空白 ▼ 查看明日开发者黄历

    0967d586466834d35b137a8aa24debd6.png

    summer

    time

    2022

    /

    07.23

    b235a3f5c08a2661a294edf29fcaa902.png

  • 相关阅读:
    【记录贴】使用项目管理软件管理大型复杂项目是种什么体验?(二)
    一种改进的樽海鞘群算法-附代码
    云计算的部署方式(公有云、私有云、混合云、社区云)
    LeetCode-46-全排列
    MySQL数据库增删改查操作和常用命令
    redis的知识
    Github星标90K?京东架构师一篇讲明白百亿级并发系统架构设计
    分布式锁的3种实现!附代码
    蛋白质宇宙
    OpenMV输出PWM,实现对舵机控制
  • 原文地址:https://blog.csdn.net/QcloudCommunity/article/details/128059648