Go编写Linktable库

a1164223711 2022-04-05 23:45:39

一、创建LinkTableNode库

  1. 为建立LinkTable以链表形式保存指令需要创建指令结点LinkTableNode。

  2. 指令结点包含:指令调用名、指令描述、接口实现指令操作以及指向下一指令的指针。

  3. 指令结点结构体的方法有:一个启用指令操作的方法。

  4. LinkTableNode还有一个创建指令结点的函数。其实现如下:

package menu
//指令结点
type LinkTableNode struct{
	cmd_s string
	desc string
	cmd_do CmdDo
	next *LinkTableNode
}
//指令操作
type CmdDo interface{
	do()
}
//节点相关 
func (this *LinkTableNode)GetNextLinkTableNode() *LinkTableNode {
	if this != nil {
		return this.next
	}
	return nil
}
func(this *LinkTableNode)Do() {
	this.cmd_do.do()
}
func CreateCmd(name string, desc string, cmd_do CmdDo) *LinkTableNode{
	return &LinkTableNode{
		cmd_s : name,
		desc : desc,
		cmd_do : cmd_do,
		next : nil,
	}
}

二、创建LinkTable库

  1. 因为Menu程序只有一个,故根据其应用场景,使用单例模式创建一个全局的List指针保存指令集合,使用sync.Do保证List只初始化一次,且在导入使用menu包时自动初始化。

  2. LinkTable结构体包含head和tail指针用于查找和插入指令到指令链表中,cnt计数记录指令个数。注:因为使用方法实现对LinkTable的一系列操作,故不需要使用互斥锁mutex,否则会造成死锁。

//指令列表
type LinkTable struct{
	head *LinkTableNode
	tail *LinkTableNode
	cnt int 	
}

3. 定义LinkTable的一系列操作,包括指令结点的增加,删除,查找,执行操作。注:Go语言有内存自动回收机制,故不需要定义手动删除释放所有结点的操作

      1)方法:Add_LinkTableNode。功能:将指令结点加入到LinkTable中。

func (this *LinkTable)Add_LinkTableNode(newC *LinkTableNode) error{
    //先看结点是否存在
	p := this.FindCmd(newC.cmd_s)
	if p != nil {
		return errors.New("Exit same command")
	}
	if this.cnt >= MAX_CMD {
		return errors.New("Full cmd!")
	}
	if this.head == nil {
		this.head = newC
		this.tail = newC
		this.cnt = 1
		return nil
	}
	this.cnt++;
	this.tail.next = newC
	this.tail = this.tail.next
	return nil
}

    2)方法:FindCmd。功能:查找指令并返回对应指针。

func (this *LinkTable)FindCmd(cmd string) *LinkTableNode {
	for p := this.head; p!= nil; p = p.next {
		if p.cmd_s == cmd {
			return p
		}
	}
	return nil
}

  3)方法:Del_LinkTableNode1。功能:根据指令名删除对应指令。

func (this *LinkTable)Del_LinkTableNode1(cmd string) error{
	if this.head == nil {
		return errors.New("Empty")
	}
	if this.head.cmd_s == cmd {
		this.head = this.head.next
		if this.head == nil {
			this.tail = this.head
		}
		return nil
	}
	pre := this.head
	for p:=pre.next; p != nil;{
		if p.cmd_s == cmd {
			pre.next = p.next
			if this.tail == p {
				this.tail = pre
			}
			return nil
		}
		pre = p
		p = p.next 
	}
	return errors.New("Doesn't exit")
}

    4)方法:GetLinkTableHead。功能:返回指令链表的头节点,因为结构体中首字母小写的属性对与包外是不可访问的。

func (this *LinkTable)GetLinkTableHead() *LinkTableNode{
	if this.head == nil {
		return nil
	}
	return this.head
}

 5)方法:GetNextLinkTableNode。功能:获取得到下一个指令结点

func (this *LinkTable)GetNextLinkTableNode(pNode *LinkTableNode) *LinkTableNode {
	if this == nil || pNode == nil {
		return nil
	}
	
	for p := this.head; p != nil; p = p.next{
		if p == pNode {
			return p.next
		}
	}
	return nil
}

 6)方法:Process。功能:执行对应指令

func (this *LinkTable)Process(cmd string) error{
	p := this.FindCmd(cmd)
	if p!= nil {
		p.Do()
		return nil
	}
	return errors.New("error cmd")
}

三、menu程序的调用

  最后为了更方便的调用运行menu程序,创建一个menu包,其中包含menu程序的一系列用户接口操作,目前实现了配置menu加入指令,以及运行menu程序的函数。

package menu
import "fmt"
func init() {
	go MenuConfig("help", "show all command and descirbe!", new(Cmd_help))
	go MenuConfig("quit", "quit the Menu!", new(Cmd_quit))
	go MenuConfig("ls", "show all file list", new(Cmd_ls))
}
func MenuConfig(cmd_s string, desc string, cmd_do CmdDo) {
	p := CreateCmd(cmd_s, desc, cmd_do)
	err := List.Add_LinkTableNode(p)
	if err != nil {
		fmt.Println(err)
	}
}
func ExecuteMenu() {
	var cmd string
	for{
		fmt.Printf("%s :", "Input a cmd number >")
		fmt.Scanln(&cmd)
		err := List.Process(cmd)
		if err != nil {
			fmt.Println(err)
		}
	}
}

四、指令操作接口的实现

  1. 使用接口实现对应指令的操作,这样指令可以分模块,更多样化的去创建,实现多态。

  2. 指令操作只需将实现的指令操作结构体,加入到对应指令节点中即可。

  3. 通过接口实现的指令操作参数可以更多样且结构体可以保存参数,也就是操作可以保存对应的配置参数。

  4. 输入参数可以使用空指针实习不定长,不定类型参数。

以下是简单指令的部分实现:

package menu
import "fmt"
 
//指令操作相关
type Cmd_help struct {
}
func (this Cmd_help)do() {
	fmt.Println("Commands in Menu:")
	p := List.head
	for p != nil {
		fmt.Printf("%s : %s \n", p.cmd_s, p.desc)
		p = p.next
	}
}
type Cmd_quit struct {
}
func (this Cmd_quit)do() {
	
	panic("Quit!")
}
type Cmd_ls struct {
}
func (this Cmd_ls)do() {
	fmt.Println("file1, file2, file3.....")
}

项目源地址

作者:313

...全文
87 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

571

社区成员

发帖
与我相关
我的任务
社区描述
软件工程教学新范式,强化专项技能训练+基于项目的学习PBL。Git仓库:https://gitee.com/mengning997/se
软件工程 高校
社区管理员
  • 码农孟宁
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧