10-Go设计模式-命令模式

命令模式

样例代码
/*
命令模式
将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;
对请求排队或者记录请求日志,以及支持可撤销的操作。
命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。
命令模式可以将请求发送者和接收者完全解耦,
发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。


命令模式的优缺点
优点:
(1) 降低系统的耦合度。
	由于请求者与接收者之间不存在直接引用,因此请求者与接收者之间实现完全解耦,相同的请求者可以对应不同的接收者,
	同样,相同的接收者也可以供不同的请求者使用,两者之间具有良好的独立性。
(2) 新的命令可以很容易地加入到系统中。
	由于增加新的具体命令类不会影响到其他类,因此增加新的具体命令类很容易,无须修改原有系统源代码,甚至客户类代码,满足“开闭原则”的要求。
(3) 可以比较容易地设计一个命令队列或宏命令(组合命令)。

缺点:
使用命令模式可能会导致某些系统有过多的具体命令类。
因为针对每一个对请求接收者的调用操作都需要设计一个具体命令类,因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。


适用场景
(1) 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
	请求调用者无须知道接收者的存在,也无须知道接收者是谁,接收者也无须关心何时被调用。
(2) 系统需要在不同的时间指定请求、将请求排队和执行请求。一个命令对象和请求的初始调用者可以有不同的生命期,
	换言之,最初的请求发出者可能已经不在了,而命令对象本身仍然是活动的,可以通过该命令对象去调用请求接收者,
	而无须关心请求调用者的存在性,可以通过请求日志文件等机制来具体实现。
(3) 系统需要将一组操作组合在一起形成宏命令。
*/

package _1_command_mode

import "fmt"

//=================================== 医生 ===================================================
//医生-命令接收者
type Doctor struct{}

func (d *Doctor) treatEye() {
	fmt.Println("医生治疗眼睛")
}

func (d *Doctor) treatNose() {
	fmt.Println("医生治疗鼻子")
}

//================================== 病单 ====================================================
//抽象的命令
type Command interface {
	Treat()
}

//治疗眼睛
type CommandTreatEye struct {
	doctor *Doctor
}

func (c *CommandTreatEye) Treat() {
	c.doctor.treatEye()
}

//治疗鼻子
type CommandTreatNose struct {
	doctor *Doctor
}

func (c *CommandTreatNose) Treat() {
	c.doctor.treatNose()
}

//护士-调用命令者
type Nurse struct {
	CmdList []Command
}

//发送病单,发送命令的方法
func (n *Nurse) Notify() {
	if n.CmdList == nil {
		return
	}

	for _, cmd := range n.CmdList {
		cmd.Treat() //执行病单绑定的命令(这里会调用病单已经绑定的医生诊断方法)
	}
}

//=================================== 病人 ========================================
func BingRen() {
	//依赖病单,通过填写病单,让医生看病
	docter := new(Doctor)
	//治疗眼睛的病单
	cmdEye := CommandTreatEye{doctor: docter}
	//治疗鼻子的病单
	cmdNose := CommandTreatNose{doctor: docter}

	//护士
	nurse := new(Nurse)
	//手机管理病单
	nurse.CmdList = append(nurse.CmdList, &cmdEye)
	nurse.CmdList = append(nurse.CmdList, &cmdNose)

	//执行病单指令
	nurse.Notify()
}

测试代码

package _1_command_mode

import "testing"

func TestDocter(t *testing.T) {
	BingRen()
}

例子

/*
练习:
路边撸串烧烤场景, 有烤羊肉,烧鸡翅命令,有烤串师傅,和服务员。
根据命令模式,设计烤串场景
*/
package _1_command_mode

import "fmt"

//=========================== 烤串师傅 ==========================
type Cooker struct{}

func (c *Cooker) MakeChicken() {
	fmt.Println("烤串师傅烤了鸡肉串")
}

func (c *Cooker) MakeChuaner() {
	fmt.Println("烤串是否烤了羊肉串")
}

//=========================== 烤串 ==============================
//抽象命令
type Command2 interface {
	Make()
}

type CommandCookChicken struct {
	cooker *Cooker
}

func (c *CommandCookChicken) Make() {
	c.cooker.MakeChicken()
}

type CommandCookChuaner struct {
	cooker *Cooker
}

func (c *CommandCookChuaner) Make() {
	c.cooker.MakeChuaner()
}

//=========================== 服务员 ============================
type WaiterMM struct {
	CmdList []Command2
}

func (w *WaiterMM) Notify() {
	if w.CmdList == nil {
		return
	}

	for _, cmd := range w.CmdList {
		cmd.Make()
	}
}

//=========================== 顾客 =============================
func consumer() {
	cooker := new(Cooker)
	cmdChicken := CommandCookChicken{cooker: cooker}
	cmdChuaner := CommandCookChuaner{cooker: cooker}

	mm := new(WaiterMM)
	mm.CmdList = append(mm.CmdList, &cmdChicken)
	mm.CmdList = append(mm.CmdList, &cmdChuaner)

	mm.Notify()
}

package _1_command_mode

import "testing"

func TestCooker(t *testing.T) {
	consumer()
}

posted @ 2022-10-17 14:34  李成果  阅读(58)  评论(0编辑  收藏  举报