数据结构和算法-Go泛型实现:第三章抽象数据类型:Go 中的面向对象编程- 2. Go 中的抽象数据类型

81 阅读3分钟

第三章:抽象数据类型:Go 中的面向对象编程

2. Go 中的抽象数据类型

在这一节中,我们将探讨如何在 Go 中创建和使用抽象数据类型(ADT)。我们将以计数器为例,并展示如何创建一个包来封装这些功能。此外,我们还将通过另一个例子来展示 ADT 的实现,并介绍使用组合的技巧。

ADT 计数器

计数器是一种简单的抽象数据类型,用于计数操作。我们将从定义一个计数器结构体开始,并为其实现相关的方法。

示例:定义 Counter 结构体

package counter

// Counter 结构体,包含一个私有的计数器变量
type Counter struct {
	count int
}

// NewCounter 返回一个新的 Counter 实例
func NewCounter() *Counter {
	return &Counter{count: 0}
}

// Increment 增加计数器的值
func (c *Counter) Increment() {
	c.count++
}

// Decrement 减少计数器的值
func (c *Counter) Decrement() {
	c.count--
}

// GetValue 返回当前计数器的值
func (c *Counter) GetValue() int {
	return c.count
}
创建 counter 包

为了组织代码和实现模块化,我们可以将计数器相关的代码放入一个名为 counter 的包中。

步骤:

  1. 创建一个名为 counter 的目录。
  2. counter 目录中创建一个名为 counter.go 的文件,并将上面的代码放入该文件中。
  3. counter.go 文件的开头添加 package counter 声明。

目录结构示例:

project/
|-- main.go
|-- counter/
    |-- counter.go
包的创建机制

在 Go 中,包是通过目录结构来组织的。每个包都位于一个单独的目录中,并且目录名称通常与包名称相同。通过这种方式,我们可以轻松地导入和使用不同的包。

示例:使用 counter 包

package main

import (
	"fmt"
	"project/counter" // 导入 counter 包
)

func main() {
	counter := counter.NewCounter()
	counter.Increment()
	fmt.Println("Counter value after increment:", counter.GetValue())
	counter.Decrement()
	fmt.Println("Counter value after decrement:", counter.GetValue())
}
实现 ADT 的另一个例子

接下来,我们将实现一个栈(Stack)抽象数据类型。栈是一种常见的数据结构,遵循后进先出(LIFO)原则。

示例:定义 Stack 结构体

package stack

import (
	"errors"
)

// Stack 结构体,包含一个存储元素的切片
type Stack[T any] struct {
	items []T
}

// NewStack 返回一个新的 Stack 实例
func NewStack[T any]() *Stack[T] {
	return &Stack[T]{items: []T{}}
}

// Push 将元素压入栈中
func (s *Stack[T]) Push(value T) {
	s.items = append(s.items, value)
}

// Pop 从栈中弹出元素
func (s *Stack[T]) Pop() (T, error) {
	if len(s.items) == 0 {
		var zero T
		return zero, errors.New("stack is empty")
	}
	value := s.items[len(s.items)-1]
	s.items = s.items[:len(s.items)-1]
	return value, nil
}

// Peek 查看栈顶元素
func (s *Stack[T]) Peek() (T, error) {
	if len(s.items) == 0 {
		var zero T
		return zero, errors.New("stack is empty")
	}
	return s.items[len(s.items)-1], nil
}

// IsEmpty 判断栈是否为空
func (s *Stack[T]) IsEmpty() bool {
	return len(s.items) == 0
}

示例:使用 Stack 结构体

package main

import (
	"fmt"
	"project/stack" // 导入 stack 包
)

func main() {
	s := stack.NewStack[int]()
	s.Push(10)
	s.Push(20)
	value, _ := s.Pop()
	fmt.Println("Popped value:", value)
	value, _ = s.Peek()
	fmt.Println("Peek value:", value)
	fmt.Println("Is stack empty?", s.IsEmpty())
}
使用组合

组合是 Go 中的一种代码复用技术,它通过将一个类型嵌入到另一个类型中,实现类型之间的组合和复用。

示例:使用组合实现扩展计数器

package extendedcounter

import (
	"project/counter" // 导入 counter 包
	"fmt"
)

// ExtendedCounter 结构体,通过组合包含 Counter
type ExtendedCounter struct {
	counter.Counter
}

// Reset 将计数器重置为零
func (ec *ExtendedCounter) Reset() {
	ec.Counter = *counter.NewCounter()
}

// PrintCurrentValue 打印当前计数器的值
func (ec *ExtendedCounter) PrintCurrentValue() {
	fmt.Println("Current Counter value:", ec.GetValue())
}

示例:使用 ExtendedCounter

package main

import (
	"project/extendedcounter" // 导入 extendedcounter 包
)

func main() {
	extCounter := &extendedcounter.ExtendedCounter{}
	extCounter.Increment()
	extCounter.PrintCurrentValue()
	extCounter.Reset()
	extCounter.PrintCurrentValue()
}

通过以上示例,我们展示了如何在 Go 中实现和使用抽象数据类型,包括通过创建包来组织代码,使用接口定义 ADT,以及通过组合实现代码复用。这些技术使得 Go 语言在缺乏传统面向对象编程特性的情况下,依然能够实现灵活和强大的数据结构和算法。