在软件开发中,设计模式是解决常见问题的最佳实践方案。命令模式作为一种行为型设计模式,它将请求封装成对象,从而将请求的发出者和执行者解耦。本文将以餐厅场景为例,讲解如何在 Golang 中运用命令模式优化代码结构,提高代码可读性和可维护性。
命令模式详解
命令模式的核心思想是将一个请求封装成一个对象,这个对象包含了执行该请求所需的所有信息,包括方法、参数等等。通过这种方式,我们可以将请求的发出者和执行者解耦,从而提高代码的灵活性。
命令模式的结构
命令模式通常包含以下几个角色:
- 命令 (Command): 定义命令的接口,声明执行命令的方法。
- 具体命令 (ConcreteCommand): 实现命令接口,并在 execute 方法中执行具体的命令逻辑。
- 调用者 (Invoker): 持有命令对象,并负责调用命令的 execute 方法。
- 接收者 (Receiver): 执行命令的实际操作者。
命令模式的优缺点
优点:
- 解耦请求和执行: 命令模式将请求的发出者和执行者解耦,提高代码的灵活性和可维护性。
- 易于扩展: 可以轻松添加新的命令,而无需修改现有代码。
- 支持撤销和重做: 可以通过保存命令的历史记录来实现命令的撤销和重做功能。
缺点:
- 可能增加代码复杂度: 如果命令过多,可能会增加代码的复杂度。
Golang 实现命令模式代码实例
定义命令接口
type Command interface {
execute()
}
创建具体命令
// 制作披萨命令
type MakePizzaCommand struct {
n int
restaurant *Restaurant
}
func (c *MakePizzaCommand) execute() {
c.restaurant.CleanedDishes -= c.n
fmt.Println("制作了", c.n, "份披萨")
}
// 制作沙拉命令
type MakeSaladCommand struct {
n int
restaurant *Restaurant
}
func (c *MakeSaladCommand) execute() {
c.restaurant.CleanedDishes -= c.n
fmt.Println("制作了", c.n, "份沙拉")
}
// 清洗餐具命令
type CleanDishesCommand struct {
restaurant *Restaurant
}
func (c *CleanDishesCommand) execute() {
c.restaurant.CleanedDishes = c.restaurant.TotalDishes
fmt.Println("餐具已清洗")
}
创建调用者
// 厨师作为调用者
type Cook struct {
Commands []Command
}
func (c *Cook) executeCommands() {
for _, command := range c.Commands {
command.execute()
}
}
创建接收者
// 餐厅作为接收者
type Restaurant struct {
TotalDishes int
CleanedDishes int
}
func NewRestaurant() *Restaurant {
const totalDishes = 10
return &Restaurant{
TotalDishes: totalDishes,
CleanedDishes: totalDishes,
}
}
// 餐厅作为命令工厂,创建具体的命令
func (r *Restaurant) MakePizza(n int) Command {
return &MakePizzaCommand{
restaurant: r,
n: n,
}
}
func (r *Restaurant) MakeSalad(n int) Command {
return &MakeSaladCommand{
restaurant: r,
n: n,
}
}
func (r *Restaurant) CleanDishes() Command {
return &CleanDishesCommand{
restaurant: r,
}
}
餐厅案例分析
假设我们有一家餐厅,有两个厨师,需要完成以下任务:
- 厨师 1:制作 2 份披萨,制作 3 份披萨,制作 4 份披萨
- 厨师 2:制作 1 份沙拉,清洗餐具,清洗餐具
我们可以使用命令模式来实现这个场景:
func main() {
// 初始化餐厅
r := NewRestaurant()
// 创建任务列表
tasks := []Command{
r.MakePizza(2),
r.MakeSalad(1),
r.MakePizza(3),
r.CleanDishes(),
r.MakePizza(4),
r.CleanDishes(),
}
// 创建厨师
cooks := []*Cook{
&Cook{},
&Cook{},
}
// 将任务分配给厨师
for i, task := range tasks {
cook := cooks[i%len(cooks)]
cook.Commands = append(cook.Commands, task)
}
// 厨师执行命令
for i, c := range cooks {
fmt.Println("厨师", i, ":")
c.executeCommands()
}
}
输出结果
厨师 0 :
制作了 2 份披萨
制作了 3 份披萨
制作了 4 份披萨
厨师 1 :
制作了 1 份沙拉
餐具已清洗
餐具已清洗
总结
命令模式是一种强大的设计模式,可以帮助我们解耦请求和执行,提高代码的灵活性和可维护性。在 Golang 中实现命令模式非常简单,只需要定义命令接口,创建具体命令、调用者和接收者即可。