Go Patterns
Mediator Pattern in Go
Behavioral Design Pattern in Go
Concept
Sometimes we have a bunch of objects that need to interact with each other. A number of objects can be large and their interactions can have complex logic.
Mediator pattern helps to organize such cases.
- Mediator lets keep loose coupling and don’t let objects have direct communication.
- Mediator keeps the interaction logic and allows to change it independently.
Let’s look for an example!
A chat
For example, we have a bunch of Users
and they want to interact with each other.
- A
User
can send a message to anotherUser
. - A
User
can send a message to allUsers
. - Some
Users
can be unavailable for messaging.
So we introduce a Chat
object that keeps this logic and organizes communication.
User type
Firstly, we will introduce a User
type. It will have a Name
and store Messages
.
type User struct {
Name string
Messages []string
}
Chat type
Then we need a Chat
type. It will have a method to Add
a User
and keep information about Users
in the Chat
.
This is our Mediator
.
type Chat struct {
Users map[string]*User
}
func NewChat() *Chat {
return &Chat{make(map[string]*User)}
}
func (c *Chat) Add(user User) {
c.Users[user.Name] = &user
}
Interaction logic
Now, let’s extend our Chat with interaction logic.
Say(to User, msg string)
- sends a message to the specificUser
and handles an error if theUser
is not in theChat
.SayAll(msg string)
- sends a message to the all availableUsers
.
type Chat struct {
Users map[string]*User
}
func (c *Chat) Say(to User, msg string) error {
user, ok := c.Users[to.Name]
if !ok {
return fmt.Errorf("%s not in the chat\n", to.Name)
}
user.Messages = append(user.Messages, msg)
return nil
}
func (c *Chat) SayAll(msg string) {
for _, user := range c.Users {
user.Messages = append(user.Messages, msg)
}
}
Usage Example
Now, we gather all pieces together to see how it works.
func main() {
// Create users
John := User{Name: "John"}
Bob := User{Name: "Bob"}
Alice := User{Name: "Alice"}
// Create chat
chat := NewChat()
// Add users to the chat
chat.Add(Bob)
chat.Add(John)
// Send messages
chat.Say(Bob, "Hello, Bob!")
chat.Say(Alice, "Hello, Alice!")
chat.SayAll("Hello, All!")
}
Conclusion
Mediator pattern is a good example of encapsulation logic into separate entity. It’s a complement to the Observer pattern. In real world applications though the logic can grow a lot, and turn this into God object antipattern.
Happy coding!
Code examples can be found in my GitHub repo pavel-fokin/go-patterns.