Go 软件设计之道:1 对不同类型的数据进行分组 #私藏项目实操分享#

简介: Go 软件设计之道:1 对不同类型的数据进行分组 #私藏项目实操分享#

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

image.png

重要的是要记住,在 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
}

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

unc (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 方法。

总结

关于声明类型的指南:

  • 声明代表新事物或独特事物的类型。
  • 不要仅仅为了可读性而创建别名。
  • 验证任何类型的值是单独创建或使用的。
  • 嵌入类型不是因为我需要状态,而是因为我们需要行为。
  • 如果我不考虑行为,我就会将自己锁定在未来不进行级联代码更改就无法成长的设计中。
  • 作为现有类型的别名或抽象的问题类型。
  • 唯一目的是共享公共状态的问题类型。


相关文章
|
1月前
|
人工智能 安全 Shell
Go并发编程避坑指南:从数据竞争到同步原语的解决方案
在高并发场景下,如钱包转账,数据一致性至关重要。本文通过实例演示了 Go 中如何利用 `sync.Mutex` 和 `sync.RWMutex` 解决数据竞争问题,帮助开发者掌握并发编程中的关键技能。
Go并发编程避坑指南:从数据竞争到同步原语的解决方案
|
9天前
|
Java 编译器 Go
【Golang】(5)Go基础的进阶知识!带你认识迭代器与类型以及声明并使用接口与泛型!
好烦好烦好烦!你是否还在为弄不懂Go中的泛型和接口而烦恼?是否还在苦恼思考迭代器的运行方式和意义?本篇文章将带你了解Go的接口与泛型,还有迭代器的使用,附送类型断言的解释
57 3
|
23天前
|
存储 监控 算法
企业电脑监控系统中基于 Go 语言的跳表结构设备数据索引算法研究
本文介绍基于Go语言的跳表算法在企业电脑监控系统中的应用,通过多层索引结构将数据查询、插入、删除操作优化至O(log n),显著提升海量设备数据管理效率,解决传统链表查询延迟问题,实现高效设备状态定位与异常筛选。
66 3
|
4月前
|
Go
Go语言同步原语与数据竞争:Mutex 与 RWMutex
在Go语言并发编程中,数据竞争是多个goroutine同时读写共享变量且未加控制导致的问题,可能引发程序崩溃或非确定性错误。为解决此问题,Go提供了`sync.Mutex`和`sync.RWMutex`两种同步机制。`Mutex`用于保护临界区,确保同一时间只有一个goroutine访问;`RWMutex`支持多读单写的细粒度控制,适合读多写少场景。使用时需避免死锁,并借助`-race`工具检测潜在的数据竞争,从而提升程序稳定性和性能。
154 51
|
4月前
|
编译器 测试技术 Go
Go语言同步原语与数据竞争:数据竞争的检测工具
本文介绍了 Go 语言中数据竞争(Data Race)的概念及其检测方法。数据竞争发生在多个 Goroutine 无同步访问共享变量且至少一个为写操作时,可能导致程序行为不稳定或偶发崩溃。Go 提供了内置的竞态检测器(Race Detector),通过 `-race` 参数可轻松检测潜在问题。文章还展示了如何使用锁或原子操作修复数据竞争,并总结了在开发和 CI 流程中启用 `-race` 的最佳实践,以提升程序稳定性和可靠性。
|
4月前
|
Go
Go语言同步原语与数据竞争:WaitGroup
本文介绍了 Go 语言中 `sync.WaitGroup` 的使用方法和注意事项。作为同步原语,它通过计数器机制帮助等待多个 goroutine 完成任务。核心方法包括 `Add()`(设置等待数量)、`Done()`(减少计数)和 `Wait()`(阻塞直到计数归零)。文章详细讲解了其基本原理、典型用法(如等待 10 个 goroutine 执行完毕),并提供了代码示例。同时指出常见错误,例如 `Add()` 必须在 goroutine 启动前调用,以及 WaitGroup 不可重复使用。最后总结了适用场景和使用要点,强调避免竞态条件与变量捕获陷阱。
|
4月前
|
安全 Go 调度
Go同步原语与数据竞争:原子操作(atomic)
本文介绍了Go语言中`sync/atomic`包的使用,帮助避免多goroutine并发操作时的数据竞争问题。原子操作是一种不可中断的操作,确保变量读写的安全性。文章详细说明了常用函数如`Load`、`Store`、`Add`和`CompareAndSwap`的功能与应用场景,并通过并发计数器示例展示了其实现方式。此外,对比了原子操作与锁的优缺点,强调原子操作适用于简单变量的高效同步,而不适合复杂数据结构。最后提醒开发者注意使用场景限制,合理选择同步工具以优化性能。
|
5月前
|
存储 JSON Go
Go语言之空接口与类型断言
本文介绍了 Go 语言中空接口(`interface{}`)和类型断言的核心概念及其应用。空接口可存储任意类型数据,适用于通用函数、动态数据结构与 JSON 解析等场景;类型断言用于将接口变量还原为具体类型,推荐使用带 `ok` 的写法以避免程序崩溃。此外,文章通过示例讲解了 `type switch` 类型判断与 JSON 处理技巧,并总结了空接口的注意事项,强调滥用可能导致类型安全性降低。内容深入浅出,帮助开发者灵活运用这些特性。
127 15
|
4月前
|
存储 JSON JavaScript
[go]byte类型, string 类型, json 类型
本文介绍了Go语言中byte类型的基本概念、特点及用法。byte是8位无符号整数,取值范围为0-255,常用于二进制数据操作,如网络通信和文件读写。文章还详细说明了byte与字符串的转换、遍历byte数据以及与其他类型间的转换。此外,探讨了Go中json.Marshal和json.Unmarshal函数实现[]byte与JSON间的转换,并对比了[]byte与JSON的区别,帮助开发者更好地理解其应用场景与差异。
155 2
|
5月前
|
算法 Go
Go语言模拟集合类型-《Go语言实战指南》
在 Go 语言中,虽然没有内建的集合(Set)类型,但可以通过 `map` 实现其功能。常用方式包括 `map[T]bool` 和更节省内存的 `map[T]struct{}`。前者以布尔值表示元素存在性,后者利用零内存开销的空结构体。文章介绍了集合的基本操作(添加、删除、判断、遍历),并通过封装示例展示如何创建自定义 Set 类型。这种实现方式适用于去重、唯一标记及集合运算等场景,简洁高效且易于扩展。

热门文章

最新文章