Go设计模式(5)-类图符号表示法

简介: 前四章讲解了Go设计模式的一些原理性知识,后面会开始讲23种设计模式。初步计划是对每种模式至少要说明含义、画类图、找实际用例、写代码实现、描述重点信息(使用场景、原因、好处等)。

前四章讲解了Go设计模式的一些原理性知识,后面会开始讲23种设计模式。初步计划是对每种模式至少要说明含义、画类图、找实际用例、写代码实现、描述重点信息(使用场景、原因、好处等)。

可能大家也看过很多设计模式相关的资料,但看完后或许有似懂非懂的感觉,我想一是因为资料中讲的例子不是特别贴合实际,另一点是因为没有真正的去实现一次代码。

所以希望经过写设计模式系列,我自己的设计模式的理解能够更加深入一些。

因为要画类图,这部分知识还是本科时候学的,大家可能也记不清了,这次就通过一个例子,把UML类图中的基本元素展示给大家,并讲一些名词定义,最后用代码实现,这样可以方便后面的讲解。

类图

此处用一个简单的例子,将类图中的各个元素表示出来。

https://www.processon.com/view/link/605be4175653bb2225e7c43f

Go设计模式(5)-类图 (1).png

名词解释

依赖关系

定义

Dependency(依赖关系)表现为函数中的参数。是类与类之间的连接,表示一个类依赖于另一个类的定义,其中一个类的变化将影响另外一个类。例如如果A依赖于B,则B体现为局部变量,方法的参数、或静态方法的调用。

表示法

虚线+箭头

实例说明

动物依赖于氧气和水,我们可以看到动物有新陈代谢函数,其入参为氧气和水,所以动物与氧气和水是依赖关系。

泛化关系

定义

Generalization(泛化)表现为继承或实现关系(is a)。具体形式为类与类之间的继承关系,接口与接口之间的继承关系,类对接口的实现关系。

即泛化关系包含继承关系和实现关系。

表示法

继承:实线+空心三角形

实现:虚线+空心三角形

实例说明

鸟是动物,所以继承自动物,大雁、鸭、企鹅也是鸟,所以继承自鸟。继承的一个重要作用是复用,动物->鸟->(大雁、鸭、企鹅)都是is a的关系。不过Go没有继承关系,继承用组合来实现。

大雁实现了飞翔接口、唐老鸭实现了讲人话接口。接口的一个主要作用是确定标准,实现了相同接口的类可以相互替换。所以并不是特别重于is a的关系。

关联关系

定义

Association关联关系表现为变量(has a )。类与类之间的联接,它使一个类知道另一个类的属性和方法。例如如果A依赖于B,则B体现为A的全局变量。关联关系有双向关联和单向关联。双向关联:两个类都知道另一个类的公共属性和操作。单向关联:只有一个类知道另外一个类的公共属性和操作。大多数关联应该是单向的,单向关系更容易建立和维护,有助于寻找可服用的类。

表示法

实线+箭头

实例说明

企鹅需要知道天气,一般是企鹅类中会有一个气候对象,两个类也是同一层次上的。

聚合关系

定义

Aggregation(聚合关系) 是关联关系的一种,是强的关联关系。聚合关系是整体和个体的关系。普通关联关系的两个类处于同一层次上,而聚合关系的两个类处于不同的层次,一个是整体,一个是部分。同时,是一种弱的“拥有”关系。体现的是A对象可以包含B对象,但B对象不是A对象的组成部分。具体表现为,如果A由B聚合成,表现为A包含有B的全局对象,但是B对象可以不在A创建的时刻创建。

表示法

空心菱形+实线+箭头

实例说明

雁群是由一堆大雁组成的,两者是不同的层次,大雁是雁群的组成部分。

合成(组合关系)

定义

Composition(组合关系)是关联关系的一种,是比聚合关系强的关系。它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期。Composition(组合关系)是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周期一致。如果A由B组成,表现为A包含有B的全局对象,并且B对象在A创建的时刻创建。

表示法

实芯菱形+实线+箭头

实例说明

翅膀是鸟的一部分,在生成鸟的时候翅膀需要一起生成,两者生命周期一样。

PS:对于依赖、泛化、关联、聚合、组合关系,其中组合、聚合、关联较为相似,关系强度上组合>聚合>关联,如果大家判断关系不属于依赖和泛化,那可以先看看两者是否是整体和部分、有相同生命周期,按照组合、聚合、关联的顺序过一遍,就能找到合适的关系了。

代码实现

上面说了一堆理论,我们还是用代码实现以下这个类图,这样对自己、对大家的帮助更大。Go没有继承,继承用组合实现。不过倒是有类对接口的实现。相关代码Go设计模式(1)-语法在这篇文章里也写过。

package main

import (
   "fmt"
   "strconv"
)

type Water struct {
   weight int64
}

type Animal struct {
   name string
}

/**
 * @Description: 动物成长
 * @receiver animal
 * @param water
 */
func (animal *Animal) Grow(water *Water) {
   weight := strconv.FormatInt(water.weight, 10)
   fmt.Printf("长大需要喝%s吨水\n", weight)
}

type Wing struct {
}

/**
 * @Description: 鸟类,伪继承Animal,组合Wing,有颜色成员变量
 */
type Bird struct {
   Animal
   featherCol string
   wing       *Wing
}

/**
 * @Description:鸟下蛋
 * @receiver bird
 */
func (bird *Bird) LayEggs() {
   fmt.Println(bird.featherCol + "鸟下蛋了哈")
}

/**
 * @Description:飞接口
 */
type Fly interface {
   fly()
}

/**
 * @Description:大雁接口,伪继承鸟,并实现Fly接口
 */
type Goose struct {
   Bird
}

func (goose *Goose) fly() {
   fmt.Println("大雁可以飞")
}

/**
 * @Description:雁群聚合大雁
 */
type Geese struct {
   geese []*Goose
}

/**
 * @Description: 雁群的飞行方式
 * @receiver g
 */
func (g *Geese) flyV() {
   fmt.Printf("雁群V形飞行,共有%d只大雁\n", len(g.geese))
}

/**
 * @Description:鸭伪继承鸟
 */
type Duck struct {
   Bird
}

/**
 * @Description:说话接口
 */
type Speak interface {
   Speak()
}

/**
 * @Description:唐老鸭伪继承鸭子
 */
type DonaldDuck struct {
   Duck
}

/**
 * @Description:唐老鸭实现说话接口
 * @receiver donaldDuck
 */
func (donaldDuck *DonaldDuck) Speak() {
   fmt.Println("唐老鸭说人话了")
}

/**
 * @Description:企鹅伪继承鸟,并关联天气
 */
type Penguin struct {
   Bird
   climate *Climate
}

/**
 * @Description:企鹅重载了下蛋接口
 * @receiver p
 */
func (p *Penguin) LayEggs() {
   fmt.Printf("企鹅在%s的天气下下蛋了\n", p.climate.content)
}

/**
 * @Description:天气类,有天气内容成员变量
 */
type Climate struct {
   content string
}

func main() {
   water := &Water{weight: 10}
   //动物
   fmt.Println("-----------动物篇-依赖关系")
   animal := &Animal{}
   animal.Grow(water) 
   //鸟
   fmt.Println("-----------鸟篇-伪继承/组合关系")
   bird := &Bird{
      featherCol: "五彩斑斓的",
      wing:       &Wing{},
   }
   bird.Grow(water) 
   bird.LayEggs()
   //大雁
   fmt.Println("-----------大雁篇-实现关系")
   goose := &Goose{}
   goose.featherCol = "黑黑的"
   goose.Grow(water)
   goose.LayEggs()
   goose.fly()
   //鸭
   fmt.Println("-----------鸭篇-伪继承关系")
   duck := &Duck{}
   duck.featherCol = "黄色的"
   duck.Grow(water)
   duck.LayEggs()
   //企鹅
   fmt.Println("-----------企鹅篇-伪继承/关联关系")
   penguin := &Penguin{}
   penguin.featherCol = "白色的"
   penguin.Grow(water)
   climate := &Climate{content: "寒冷的"}
   penguin.climate = climate
   penguin.LayEggs()
   //雁群
   fmt.Println("-----------雁群篇-聚合关系")
   g := &Geese{}
   g.geese = append(g.geese, goose)
   g.flyV()
   //唐老鸭
   fmt.Println("-----------唐老鸭篇-实现关系")
   donaldDuck := &DonaldDuck{}
   donaldDuck.featherCol = "红色的"
   donaldDuck.Grow(water)
   donaldDuck.LayEggs()
   donaldDuck.Speak()
}

输出:

➜ myproject go run main.go
-----------动物篇-依赖关系

长大需要喝10吨水

-----------鸟篇-伪继承/组合关系

长大需要喝10吨水

五彩斑斓的鸟下蛋了哈

-----------大雁篇-实现关系

长大需要喝10吨水

黑黑的鸟下蛋了哈

大雁可以飞

-----------鸭篇-伪继承关系

长大需要喝10吨水

黄色的鸟下蛋了哈

-----------企鹅篇-伪继承/关联关系

长大需要喝10吨水

企鹅在寒冷的的天气下下蛋了

-----------雁群篇-聚合关系

雁群V形飞行,共有1只大雁

-----------唐老鸭篇-实现关系

长大需要喝10吨水

红色的鸟下蛋了哈

唐老鸭说人话了

虽然这只是一个简单的示例,但是也能阐明很多知识点

  1. 可以看到Go使用匿名组合实现了伪继承的效果,对于Grow函数可以复用
  2. 匿名组合使用的不是指针类型,基类采用指针方式的组合,依然具有派生的效果,只是派生类创建实例的时候需要外部提供一个基类实例的指针
  3. 大雁和唐老鸭分别实现了飞翔和说话接口,如果有其它类也实现了接口,大雁和唐老鸭可以对其进行替换
  4. 关于泛化(伪继承/实现)、依赖、组合、聚合、关联,代码中对这些细节都有讲述

代码位置:https://github.com/shidawuhen/asap/blob/master/controller/design/5uml.go

总结

虽然工作任务重、实际工作中用到的时候比较少,但是对这些知识有一个了解,会在未来的某刻帮助解决很多问题。如果大家喜欢这篇文章,请点赞转发。

资料

  1. https://blog.csdn.net/hitwhylz/article/details/24483061
  2. https://blog.csdn.net/weixin_34709138/article/details/114534674
  3. https://blog.csdn.net/maybehelios/article/details/2038685
  4. 大话设计模式
  5. https://zhuanlan.zhihu.com/p/53846600

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我的个人博客为:https://shidawuhen.github.io/

往期文章回顾:

设计模式

  1. Go设计模式(4)-代码编写优化
  2. Go设计模式(4)-代码编写
  3. Go设计模式(3)-设计原则
  4. Go设计模式(2)-面向对象分析与设计
  5. Go设计模式(1)-语法

语言

  1. Go工具之generate
  2. Go单例实现方案
  3. Go通道实现原理
  4. Go定时器实现原理
  5. Beego框架使用
  6. Golang源码BUG追查
  7. Gin框架简洁版
  8. Gin源码剖析

架构

  1. 支付接入常规问题
  2. 限流实现2
  3. 秒杀系统
  4. 分布式系统与一致性协议
  5. 微服务之服务框架和注册中心
  6. 浅谈微服务
  7. 限流实现1
  8. CDN请求过程详解
  9. 常用缓存技巧
  10. 如何高效对接第三方支付
  11. 算法总结

存储

  1. MySQL开发规范
  2. Redis实现分布式锁
  3. 事务原子性、一致性、持久性的实现原理
  4. InnoDB锁与事务简析

网络

  1. HTTP2.0基础教程
  2. HTTPS配置实战
  3. HTTPS连接过程
  4. TCP性能优化

读书笔记

  1. 原则
  2. 资治通鉴
  3. 敏捷革命
  4. 如何锻炼自己的记忆力
  5. 简单的逻辑学-读后感
  6. 热风-读后感
  7. 论语-读后感
  8. 孙子兵法-读后感

思考

  1. 为动员一切力量争取抗战胜利而斗争
  2. 反对自由主义
  3. 实践论
  4. 评价自己的标准
  5. 服务端团队假期值班方案
  6. 项目流程管理
  7. 对项目管理的一些看法
  8. 对产品经理的一些思考
  9. 关于程序员职业发展的思考
  10. 关于代码review的思考
  11. Markdown编辑器推荐-typora
相关文章
|
3月前
|
设计模式 Go
go 设计模式之观察者模式
go 设计模式之观察者模式
|
4月前
|
设计模式 Go
Go语言设计模式:使用Option模式简化类的初始化
在Go语言中,面对构造函数参数过多导致的复杂性问题,可以采用Option模式。Option模式通过函数选项提供灵活的配置,增强了构造函数的可读性和可扩展性。以`Foo`为例,通过定义如`WithName`、`WithAge`、`WithDB`等设置器函数,调用者可以选择性地传递所需参数,避免了记忆参数顺序和类型。这种模式提升了代码的维护性和灵活性,特别是在处理多配置场景时。
72 8
|
6月前
|
设计模式 Go
[设计模式 Go实现] 结构型~享元模式
[设计模式 Go实现] 结构型~享元模式
|
6月前
|
设计模式 Go API
[设计模式 Go实现] 结构型~外观模式
[设计模式 Go实现] 结构型~外观模式
|
6月前
|
设计模式 Go
[设计模式 Go实现] 结构型~组合模式
[设计模式 Go实现] 结构型~组合模式
|
6月前
|
设计模式 Go
[设计模式 Go实现] 行为型~迭代器模式
[设计模式 Go实现] 行为型~迭代器模式
|
6月前
|
设计模式 存储 Go
[设计模式 Go实现] 行为型~备忘录模式
[设计模式 Go实现] 行为型~备忘录模式
|
6月前
|
设计模式 Go
[设计模式 Go实现] 结构型~装饰模式
[设计模式 Go实现] 结构型~装饰模式
|
6月前
|
设计模式 Go
[设计模式 Go实现] 行为型~解释器模式
[设计模式 Go实现] 行为型~解释器模式
|
6月前
|
设计模式 Go 网络安全
[设计模式 Go实现] 结构型~代理模式
[设计模式 Go实现] 结构型~代理模式