【转】Command Pattern in Go (Golang)
原文: https://www.sohamkamani.com/golang/command-pattern/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | package main import "fmt" // The restaurant contains the total dishes and the total cleaned dishes type Restaurant struct { TotalDishes int CleanedDishes int } // `NewRestaurant` constructs a new restaurant instance with 10 dishes, // all of them being clean func NewResteraunt() *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, } } type Command interface { execute() } // The MakePizzaCommand is a struct which contains // the number of pizzas to make, as well as the // restaurant as its attributes type MakePizzaCommand struct { n int restaurant *Restaurant } func (c *MakePizzaCommand) execute() { // Reduce the total clean dishes of the restaurant // and print a message once done c.restaurant.CleanedDishes -= c.n fmt.Println( "made" , c.n, "pizzas" ) } // The MakeSaladCommand is similar to the MakePizza command type MakeSaladCommand struct { n int restaurant *Restaurant } func (c *MakeSaladCommand) execute() { c.restaurant.CleanedDishes -= c.n fmt.Println( "made" , c.n, "salads" ) } type CleanDishesCommand struct { restaurant *Restaurant } func (c *CleanDishesCommand) execute() { // Reset the cleaned dishes to the total dishes // present, and print a message once done c.restaurant.CleanedDishes = c.restaurant.TotalDishes fmt.Println( "dishes cleaned" ) } // A Cook comes with their list of commands as attributes type Cook struct { Commands []Command } // The executeCommands method executes all the commands // one by one func (c *Cook) executeCommands() { for _, c := range c.Commands { c.execute() } } func main() { // initialize a new resaurant r := NewResteraunt() // create the list of tasks to be executed tasks := []Command{ r.MakePizza(2), r.MakeSalad(1), r.MakePizza(3), r.CleanDishes(), r.MakePizza(4), r.CleanDishes(), } // create the cooks that will execute the tasks cooks := []*Cook{ &Cook{}, &Cook{}, } // Assign tasks to cooks alternating between the existing // cooks. for i, task := range tasks { // Using the modulus of the current task index, we can // alternate between different cooks cook := cooks[i%len(cooks)] cook.Commands = append(cook.Commands, task) } // Now that all the cooks have their commands, we can call // the `executeCommands` method that will have each cook // execute their respective commands for i, c := range cooks { fmt.Println( "cook" , i, ":" ) c.executeCommands() } } |
--------------------
Command Pattern in Go (Golang)
This article will explain the command pattern, where to use it, and how to implement it in Go.
The command pattern, as the name suggests, is used when we want to create and execute “commands”. Different commands have their own implementation, but have the same steps for execution.
The Command Interface
The basic unit for implementing the command pattern is the Command interface:
type Command interface {
execute()
}
If the command can error out, the interface can contain an error return value as well:
type Command interface {
execute() error
}
👉 The command interface provides a generic signature which any other type can implement
Let’s look at an example to illustrate the command pattern in Go.
Consider a restaurant, which has a certain number of cooks, and dishes in the kitchen. Each cook can perform one of the following tasks at a time:
- Cook pizza
- Make salad
- Wash dishes Every time a pizza or salad is made, a dish is used up. Washing the dishes resets the total number of dishes.
Creating Commands
The three tasks for the restaurant can each be represented as commands. Let’s see how we can construct the restaurant and the three commands:
// The restaurant contains the total dishes and the total cleaned dishes
type Restaurant struct {
TotalDishes int
CleanedDishes int
}
// `NewRestaurant` constructs a new restaurant instance with 10 dishes,
// all of them being clean
func NewResteraunt() *Restaurant {
const totalDishes = 10
return &Restaurant{
TotalDishes: totalDishes,
CleanedDishes: totalDishes,
}
}
// The MakePizzaCommand is a struct which contains
// the number of pizzas to make, as well as the
// restaurant as its attributes
type MakePizzaCommand struct {
n int
restaurant *Restaurant
}
func (c *MakePizzaCommand) execute() {
// Reduce the total clean dishes of the restaurant
// and print a message once done
c.restaurant.CleanedDishes -= c.n
fmt.Println("made", c.n, "pizzas")
}
// The MakeSaladCommand is similar to the MakePizza command
type MakeSaladCommand struct {
n int
restaurant *Restaurant
}
func (c *MakeSaladCommand) execute() {
c.restaurant.CleanedDishes -= c.n
fmt.Println("made", c.n, "salads")
}
type CleanDishesCommand struct {
restaurant *Restaurant
}
func (c *CleanDishesCommand) execute() {
// Reset the cleaned dishes to the total dishes
// present, and print a message once done
c.restaurant.CleanedDishes = c.restaurant.TotalDishes
fmt.Println("dishes cleaned")
}
👉
MakePizzaCommand
,MakeSaladCommand
, andCleanDishesCommand
all implement theCommand
interface with theirexecute
method.
We can now add methods to the Restaurant
in order to create instances of these commands:
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,
}
}
In this way, the Restaurant
acts as a kind of factory for commands.
Executing Commands
Once a command is created, it can be executed by calling the execute
method. Although this may seem simple, it has great value when we need to execute multiple different commands.
To demonstrate this, let’s add in some cooks for our restaurant:
// A Cook comes with their list of commands as attributes
type Cook struct {
Commands []Command
}
// The executeCommands method executes all the commands
// one by one
func (c *Cook) executeCommands() {
for _, c := range c.Commands {
c.execute()
}
}
The Cook
is the executor of our restaurant, accepting commands and executing them one after the other.
👉 Having the
Cook
take a set ofCommand
object separates them from the actual execution of the commands, since cooks don’t need to know the internal implementation of each command
Putting It All Together
So far, we have three entities in our example:
- The Restaurant, on which the commands execute
- The Cooks, which execute the commands
- The Commands themselves
Using these three entities, we can construct a job queue for each cook to execute their respective command on the restaurant:
func main() {
// initialize a new resaurant
r := NewResteraunt()
// create the list of tasks to be executed
tasks := []Command{
r.MakePizza(2),
r.MakeSalad(1),
r.MakePizza(3),
r.CleanDishes(),
r.MakePizza(4),
r.CleanDishes(),
}
// create the cooks that will execute the tasks
cooks := []*Cook{
&Cook{},
&Cook{},
}
// Assign tasks to cooks alternating between the existing
// cooks.
for i, task := range tasks {
// Using the modulus of the current task index, we can
// alternate between different cooks
cook := cooks[i%len(cooks)]
cook.Commands = append(cook.Commands, task)
}
// Now that all the cooks have their commands, we can call
// the `executeCommands` method that will have each cook
// execute their respective commands
for i, c := range cooks {
fmt.Println("cook", i, ":")
c.executeCommands()
}
}
You can run the complete example here
This will give you the output:
cook 0 :
made 2 pizzas
made 3 pizzas
made 4 pizzas
cook 1 :
made 1 salads
dishes cleaned
dishes cleaned
When to Use Command Pattern
The command pattern is useful when you need to execute tasks, but you want to separate the tasks management from the execution of the task itself.
In the example illustrated in this post, we separated the executors (the cooks) from the tasks by encapsulating each task in a common interface.
Can you think of any other cases where command patterns would be useful? Let me know in the comments!
Here are some other Golang design patterns that I’ve covered:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
2023-03-12 【转】Golang-RPC(五):golang的net-rpc结合protobuf实现rpc通讯
2023-03-12 【转】git帮助阅读开源项目
2023-03-12 【转】Golang Reflect反射的使用详解1 --- makeFunc的使用
2020-03-12 【转】golang 并发程序写入map两种实现方式sync.Mutex和chan的效率对比
2018-03-12 php 获取TZ时间格式
2014-03-12 html 页面视图中的资源文件(css/js/image)的路径问题。