第37天:综合复习与小项目

今天我们将通过完成一个小项目来综合复习之前所学的Go语言知识。本次课程的目标是巩固前面学到的基础与高级概念,涉及Go语言的变量、数据类型、函数、结构体、接口、并发、模块管理等内容。通过一个实战项目,你将学习如何将这些知识应用到实际开发中,并为未来的Go语言开发打下坚实的基础。

我们将开发一个简单的任务管理系统(To-Do List),这个项目的主要功能包括:

  1. 添加任务
  2. 列出所有任务
  3. 标记任务为已完成
  4. 删除任务
  5. 保存和加载任务列表到文件

该项目将涵盖以下内容:

  • Go的数据结构(切片、结构体)
  • 文件读写操作
  • Go并发与同步
  • Go模块化管理
  • 单元测试

一、项目需求分析

1. 功能列表

2. 项目结构

该项目的代码将遵循模块化设计,方便扩展和维护。目录结构如下:

/todo-app
    /main.go         # 主程序入口
    /todo.go         # 任务结构体及相关操作
    /storage.go      # 任务的保存与加载逻辑
    /todo_test.go    # 单元测试文件
    /README.md       # 项目说明文档

二、设计思路与实现步骤

1. 数据结构设计

任务结构体

首先,我们定义任务的基本信息,每个任务包括以下几个字段:

  • ID:任务的唯一标识
  • 名称:任务的描述
  • 是否完成:表示任务的完成状态
package todo

import "fmt"

// Task 表示一个待办事项
type Task struct {
	ID       int    // 任务ID
	Name     string // 任务描述
	Complete bool   // 是否完成
}

// String 实现 Task 的字符串表示
func (t Task) String() string {
	status := "未完成"
	if t.Complete {
		status = "已完成"
	}
	return fmt.Sprintf("[%d] %s - %s", t.ID, t.Name, status)
}
任务管理切片

我们使用切片来管理多个任务。任务列表以切片的形式存储,提供添加、删除、标记完成等操作。

package todo

// TaskList 管理任务的列表
type TaskList struct {
	Tasks []Task
}

// AddTask 添加新任务
func (tl *TaskList) AddTask(name string) {
	task := Task{
		ID:       len(tl.Tasks) + 1,
		Name:     name,
		Complete: false,
	}
	tl.Tasks = append(tl.Tasks, task)
}

// ListTasks 列出所有任务
func (tl *TaskList) ListTasks() []Task {
	return tl.Tasks
}

// CompleteTask 标记任务为完成
func (tl *TaskList) CompleteTask(id int) {
	for i, task := range tl.Tasks {
		if task.ID == id {
			tl.Tasks[i].Complete = true
			break
		}
	}
}

// DeleteTask 删除任务
func (tl *TaskList) DeleteTask(id int) {
	for i, task := range tl.Tasks {
		if task.ID == id {
			tl.Tasks = append(tl.Tasks[:i], tl.Tasks[i+1:]...)
			break
		}
	}
}

2. 文件读写操作

为了保持任务列表的持久性,我们需要实现任务的保存和加载。这里我们使用 encoding/json 包将任务列表序列化为 JSON 格式并写入文件,加载时则反序列化。

package todo

import (
	"encoding/json"
	"io/ioutil"
	"os"
)

// SaveTasks 将任务保存到文件
func (tl *TaskList) SaveTasks(filename string) error {
	data, err := json.Marshal(tl.Tasks)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(filename, data, 0644)
}

// LoadTasks 从文件加载任务
func (tl *TaskList) LoadTasks(filename string) error {
	file, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	data, err := ioutil.ReadAll(file)
	if err != nil {
		return err
	}

	return json.Unmarshal(data, &tl.Tasks)
}

3. 并发与任务操作

为了提高程序的响应速度,我们可以使用Go语言的并发特性,比如在任务加载和保存时使用 Goroutine。

package todo

import (
	"log"
	"sync"
)

// SaveTasksConcurrently 并发保存任务
func (tl *TaskList) SaveTasksConcurrently(filename string) {
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		if err := tl.SaveTasks(filename); err != nil {
			log.Println("任务保存失败:", err)
		}
	}()
	wg.Wait()
}

// LoadTasksConcurrently 并发加载任务
func (tl *TaskList) LoadTasksConcurrently(filename string) {
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		if err := tl.LoadTasks(filename); err != nil {
			log.Println("任务加载失败:", err)
		}
	}()
	wg.Wait()
}

4. 用户交互界面

在命令行界面中,我们可以通过菜单方式让用户与任务系统进行交互。以下是命令行界面的简单实现。

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
	"todo-app/todo"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	tasks := &todo.TaskList{}
	filename := "tasks.json"

	// 加载之前保存的任务
	tasks.LoadTasks(filename)

	for {
		fmt.Println("\n=== 任务管理系统 ===")
		fmt.Println("1. 添加任务")
		fmt.Println("2. 列出所有任务")
		fmt.Println("3. 完成任务")
		fmt.Println("4. 删除任务")
		fmt.Println("5. 保存并退出")

		choice, _ := reader.ReadString('\n')
		choice = strings.TrimSpace(choice)

		switch choice {
		case "1":
			fmt.Println("请输入任务名称:")
			name, _ := reader.ReadString('\n')
			name = strings.TrimSpace(name)
			tasks.AddTask(name)
			fmt.Println("任务已添加!")
		case "2":
			fmt.Println("当前任务列表:")
			for _, task := range tasks.ListTasks() {
				fmt.Println(task)
			}
		case "3":
			fmt.Println("请输入要完成的任务ID:")
			idInput, _ := reader.ReadString('\n')
			id, _ := strconv.Atoi(strings.TrimSpace(idInput))
			tasks.CompleteTask(id)
			fmt.Println("任务已标记为完成!")
		case "4":
			fmt.Println("请输入要删除的任务ID:")
			idInput, _ := reader.ReadString('\n')
			id, _ := strconv.Atoi(strings.TrimSpace(idInput))
			tasks.DeleteTask(id)
			fmt.Println("任务已删除!")
		case "5":
			tasks.SaveTasks(filename)
			fmt.Println("任务列表已保存。")
			return
		default:
			fmt.Println("无效的选择,请重新输入。")
		}
	}
}

三、项目功能展示与代码运行流程

1. 任务添加

用户输入任务名称,程序将其添加到任务列表中,并显示所有任务。

2. 任务完成

用户输入任务的ID,程序将其标记为已完成,并更新任务状态。

3. 任务删除

用户输入任务ID,程序删除该任务并显示更新后的任务列表。

4. 保存与加载

程序启动时自动从文件中加载任务列表,退出时保存当前的任务列表到文件中。

5. 并发操作

通过并发方式加载和保存任务,提升程序的响应速度。


四、代码运行流程图

下图展示了整个程序的执行流程:

+----------------------------------+
|           程序启动                |
+----------------------------------+
            |
            v
+-----------------------+          +------------------------+
| 加载任务列表            |----->----| 文件加载成功             |
+-----------------------+          +------------------------+
            |
            v
+-----------------------+
| 显示主菜单              |
+-----------------------+
            |
            v
+-----------------------------------------------+
| 用户选择操作(添加、列出、完成、删除、退出)          |
+-----------------------------------------------+
            |
            v
+-----------------------+
| 执行相应的操作           |
+-----------------------+
            |
            v
+----------------------------------+
| 保存任务并退出程序                  |
+----------------------------------+

五、项目扩展方向

  1. 增加定时提醒功能:可以为每个任务设置截止时间,并在超时后提醒用户。
  2. 增加任务优先级:为任务分配优先级,帮助用户更好地管理待办事项。
  3. 引入REST API:可以将任务管理系统改为Web应用,提供RESTful API接口,便于其他系统集成。

六、总结

通过本次小项目的开发,我们巩固了Go语言的多项核心知识,包括结构体、切片、并发、文件操作以及模块化管理。这个项目展示了如何将Go语言的基本概念应用到实际项目中,并通过命令行界面与用户进行交互。


怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

11-08 11:58