Go 软件设计之道

简介: 重要的是要记住,在 Go 中,子类型或子类的概念真的不存在,这些设计模式应该被避免。

对不同类型的数据进行分组

重要的是要记住,在 Go 中,子类型或子类的概念真的不存在,这些设计模式应该被避免。


以下是不应该遵循或实施的反模式:

type Animal struct {
    Name string
    IsMammal bool
}

动物类型被声明为一个基础类型,它试图定义所有动物共有的数据。我也尝试向动物提供一些常见的行为。

func (a *Animal) Speak() {
    fmt.Println("UGH!",
    "My name is", a.Name, ", it is", a.IsMammal, "I am a mammal")
}

大多数动物都有以这种或那种方式说话的能力。然而,试图将这种常见的行为仅仅适用于一种动物并没有任何意义。在这一点上,我不知道这种动物发出什么声音,所以我写了 UGH

type Dog struct {
    Animal
    PackFactor int
}


现在,真正的问题开始了。我试图用嵌入的方式使狗拥有动物的一切,而且更多。从表面上看,这似乎是可行的,但也会有问题。既然如此,狗确实有一种特殊的说话方式

func (d *Dog) Speak() {
    fmt.Println("Woof!",
    "My name is", d.Name,
    ", it is", d.IsMammal,
    "I am a mammal with a pack factor of", d.PackFactor)
}


在说话方法的实现中,我可以把 UGH 换成 Woof 。这就是 具体到狗的说话方式

type Cat struct {
    Animal
    ClimbFactor int
}


如果我打算养一只代表动物的狗,那么我就必须养一只猫。使用嵌入法,猫是动物的一切,而且更多。

func (c *Cat) Speak() {
    fmt.Println("Meow!",
    "My name is", c.Name,
    ", it is", c.IsMammal,
    "I am a mammal with a climb factor of", c.ClimbFactor)
}


在实现 Speak 的方法中,我可以把 UGH 改成 Meow 。这就是具体到猫的说话方式


一切看起来都很好,看起来嵌入提供的功能与其他语言中的继承相同。然后我试着把狗和猫分组,因为它们有共同的 DNA,都是动物。

// This does not compile, a Dog and Cat are not an Animal.
animals := []Animal{
    Dog{
        Animal: Animal{
            Name: "Fido",
            IsMammal: true,
        },
        PackFactor: 5,
    },
    Cat{
        Animal: Animal{
        Name: "Milo",
        IsMammal: true,
    },
    ClimbFactor: 4,
    },
}
for _, animal := range animals {
    animal.Speak()
}


当我尝试这样做时,编译器抱怨狗和猫不是动物,这是真的。 嵌入与继承不同,这是我需要远离的模式。 狗是狗,猫是猫,动物是动物。 我不能把狗和猫当作动物来传递,因为它们不是。


这种机制也不是很灵活。 它需要开发人员进行配置,除非我可以访问代码并且可以随着时间的推移进行配置更改,否则这不是很灵活


如果这不是我们构建 Dog 和 Cat 集合的方式,我们如何在 Go 中做到这一点? 这不是通过共同的 DNA 进行分组,而是通过共同的行为进行分组。 行为是关键。

type Speaker interface {
    Speak()
}


如果我使用一个接口,那么我可以定义我想要对不同类型的数据进行分组的通用行为方法集:

speakers := []Speaker{
    &Dog{
        Animal: Animal{
            Name: "Fido",
            IsMammal: true,
        },
        PackFactor: 5,
    },
    &Cat{
        Animal: Animal{
            Name: "Milo",
            IsMammal: true,
        },
        ClimbFactor: 4,
    },
}
for _, speaker := range speakers {
    speaker.Speak()
}


在新的代码中,我现在可以根据狗和猫的共同行为集将它们分组。的行为,也就是狗和猫能说话的事实,把它们归为一类。


事实上,Animal 类型确实是类型污染,因为声明一个类型只是为了共享一组公共状态是一种方式,应该避免.

type Dog struct {
    Name string
    IsMammal bool
    PackFactor int
}
type Cat struct {
    Name string
    IsMammal bool
    ClimbFactor int
}


在这种特殊情况下,我宁愿看到动物类型被删除,字段被复制并粘贴到狗和猫类型中。稍后我将有关于更好的模式的说明,以消除这些情况的发生。


以下是原始代码中的代码异味:


  • Animal 类型提供了一个可重用状态的抽象层。
  • 程序永远不需要创建或单独使用 Animal 类型的值。
  • 泛化了 Animal 类型的 Speak 方法的实现。
  • 永远不会调用 Animal 类型的 Speak 方法。


总结

关于声明类型的指南:


  • 声明代表新事物或独特事物的类型。
  • 不要仅仅为了可读性而创建别名。
  • 验证任何类型的值是单独创建或使用的。
  • 嵌入类型不是因为我需要状态,而是因为我们需要行为。
  • 如果我不考虑行为,我就会将自己锁定在未来不进行级联代码更改就无法成长的设计中。
  • 作为现有类型的别名或抽象的问题类型。
  • 唯一目的是共享公共状态的问题类型。
相关文章
|
存储 Go 数据处理
Go 语言整洁架构实践
Go 语言整洁架构实践
110 0
|
Cloud Native 中间件 Java
Go语言学习路线 - 8.高级篇:从五个问题来畅想Go工程师的未来发展
有人常说,编程语言对软件工程师来说并不重要,更重要的是软件工程思想、架构设计能力等更高层面的内容。 这个观点本身没有问题,但它更多的是针对有相当工作经验的程序员。对于绝大多数的人,编程语言依然是最重要、最核心的技能,也是通往更高层面的敲门砖。所以,精通一门编程语言,不仅仅要熟悉其语法与原理,更要了解其周边的生态,包括框架、开源库、中间件等,以及掌握它适用的业务场景。
158 0
|
6月前
|
SQL 关系型数据库 测试技术
技术经验分享:Go开源项目
技术经验分享:Go开源项目
45 0
|
4月前
|
人工智能 编译器 Go
Go 哪里没有做好?Rob Pike 深刻反思了
Go 哪里没有做好?Rob Pike 深刻反思了
|
4月前
|
Go API 数据库
[go 面试] 分布式事务框架选择与实践
[go 面试] 分布式事务框架选择与实践
|
4月前
|
设计模式 安全 Go
|
4月前
|
安全 Java 测试技术
Go 高性能编程心法探秘
文章深入探讨了Go语言在高性能编程中的各种技巧,包括常用数据结构的使用、内存管理、并发编程策略,以及如何通过减少锁的使用、有效利用sync包中的工具来优化程序性能。
22 0
|
7月前
|
JSON 前端开发 Go
lucky - go 语言实现的快速开发平台
go 语言实现的快速开发平台,自动生成crud代码,前端页面通过json配置,无需编写前端代码。
55 0
|
7月前
|
Go 云计算 开发者
2024 Python开发者转型Go开发
随着Go语言在云计算、微服务和高性能网络服务中的流行,Python开发者面临是否转向Go开发的选择。这个决定涉及到多方面的考量,包括语言特性、生态系统、性能需求、学习曲线和职业发展等。本文将深入探讨Python开发者转向Go开发的利弊,分析两种语言在不同场景下的适用性,并提供从Python到Go的过渡策略,旨在为Python开发者提供全面的转型指南。
100 0
2024 Python开发者转型Go开发
|
Java 编译器 Go
为何每个开发者都在谈论Go?1
为何每个开发者都在谈论Go?
114 0