第37天:综合复习与小项目
今天我们将通过完成一个小项目来综合复习之前所学的Go语言知识。本次课程的目标是巩固前面学到的基础与高级概念,涉及Go语言的变量、数据类型、函数、结构体、接口、并发、模块管理等内容。通过一个实战项目,你将学习如何将这些知识应用到实际开发中,并为未来的Go语言开发打下坚实的基础。
我们将开发一个简单的任务管理系统(To-Do List),这个项目的主要功能包括:
- 添加任务
- 列出所有任务
- 标记任务为已完成
- 删除任务
- 保存和加载任务列表到文件
该项目将涵盖以下内容:
- Go的数据结构(切片、结构体)
- 文件读写操作
- Go并发与同步
- Go模块化管理
- 单元测试
一、项目需求分析
1. 功能列表
功能 | 描述 |
---|---|
添加任务 | 用户可以添加一个任务到待办事项列表中 |
列出所有任务 | 列出当前的所有任务,并显示每个任务的状态(完成/未完成) |
标记任务为完成 | 用户可以标记某个任务为已完成状态 |
删除任务 | 用户可以删除某个任务 |
保存任务列表 | 将任务列表保存到文件中,以便下次运行时加载 |
加载任务列表 | 启动时从文件加载之前保存的任务列表 |
2. 项目结构
该项目的代码将遵循模块化设计,方便扩展和维护。目录结构如下:
/todo-app
/main.go # 主程序入口
/todo.go # 任务结构体及相关操作
/storage.go # 任务的保存与加载逻辑
/todo_test.go # 单元测试文件
/README.md # 项目说明文档
二、设计思路与实现步骤
1. 数据结构设计
任务结构体
首先,我们定义任务的基本信息,每个任务包括以下几个字段:
- ID:任务的唯一标识
- 名称:任务的描述
- 是否完成:表示任务的完成状态
go
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)
}
任务管理切片
我们使用切片来管理多个任务。任务列表以切片的形式存储,提供添加、删除、标记完成等操作。
go
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 格式并写入文件,加载时则反序列化。
go
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。
go
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. 用户交互界面
在命令行界面中,我们可以通过菜单方式让用户与任务系统进行交互。以下是命令行界面的简单实现。
go
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
+----------------------------------+
| 保存任务并退出程序 |
+----------------------------------+
五、项目扩展方向
- 增加定时提醒功能:可以为每个任务设置截止时间,并在超时后提醒用户。
- 增加任务优先级:为任务分配优先级,帮助用户更好地管理待办事项。
- 引入REST API:可以将任务管理系统改为Web应用,提供RESTful API接口,便于其他系统集成。
六、总结
通过本次小项目的开发,我们巩固了Go语言的多项核心知识,包括结构体、切片、并发、文件操作以及模块化管理。这个项目展示了如何将Go语言的基本概念应用到实际项目中,并通过命令行界面与用户进行交互。
怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!