Ha Noi, on Thursday 30/08/2023
At the moment, a little speech to give to the present...
The cafe, the cigarette, and some rainy days...
Listening to this song, I miss the memories of when we were young, stupid children, and we still loved. When the cafe has no sugar, I often drink something that is sweet. Now, it has a pale taste, like water, or if it has a taste, then the taste is the memories or the regret of all the great old days.
I. Decorator pattern
Decorator is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.
Using decorators you can wrap objects countless number of times since both target objects and decorators follow the same interface. The resulting object will get a stacking behavior of all wrappers.
Reference: Decorator pattern
II. Implement
1. The problem
We need to build a system for the notification when the user has the action: create an account, update an account, or delete an account,... and for each action, we will send the notification to some platforms, eg: Slack, SMS, Facebook,...
Example:
-
When a user creates an account. The system will send the notification to the user's phone by SMS.
-
When a user updates the user's account, eg: password, and name,... then we will send the notification to your SMS, your Facebook, and our Slack.
-
When a user deletes the user's account then we will send the notification to your SMS, our Slack.
Note: The system will have a lot of actions and platforms to receive notifications. For each action, we must send a lot of platforms.
2. Problem solving
When we have a new action and will send the notification to some platforms. We tried to address that problem by creating special subclasses which combined several notification methods within one class. However, it quickly became apparent that this approach would bloat the code immensely, not only the library code but the client code as well.
So, to resolve the problem, we will use the decorator design pattern.
3. Implement decorator design pattern
3.1. Component
package component type INotifier interface { SendNotification(msg string)
}
3.2. Concrete component
This is a folder that handles common logic, eg: getting common information from the database, config, or some common action,...
package concrete_component import ( "fmt" "time"
) type Notifier struct {
} func (n *Notifier) SendNotification(msg string) { fmt.Println("Handle process logic common...") time.Sleep(2 * time.Second)
}
3.3. Concrete decorator
This is a folder that handles and implements sending the notification to each platform, eg: Slack notification, SMS notification, and Facebook notification,...
package concrete_decorator import ( "fmt" "time" "design-pattern-golang-example/decorator-pattern/component"
) type FacebookNotifier struct { Notifier component.INotifier
} func (f *FacebookNotifier) SendNotification(msg string) { f.Notifier.SendNotification(msg) fmt.Println(fmt.Sprintf("Send the notification to Facebook with message %s", msg)) time.Sleep(2 * time.Second) fmt.Println("Done")
}
package concrete_decorator import ( "fmt" "time" "design-pattern-golang-example/decorator-pattern/component"
) type SlackNotifier struct { Notifier component.INotifier
} func (s *SlackNotifier) SendNotification(msg string) { s.Notifier.SendNotification(msg) fmt.Println(fmt.Sprintf("Send the notification to Slack with message %s", msg)) time.Sleep(2 * time.Second) fmt.Println("Done")
}
package concrete_decorator import ( "fmt" "time" "design-pattern-golang-example/decorator-pattern/component"
) type SmsNotifier struct { Notifier component.INotifier
} func (s *SmsNotifier) SendNotification(msg string) { s.Notifier.SendNotification(msg) fmt.Println(fmt.Sprintf("Send the notification to SMS with message %s", msg)) time.Sleep(2 * time.Second) fmt.Println("Done")
}
3.4. Main function and the result
package main import ( "fmt" "time" concreteComponent "design-pattern-golang-example/decorator-pattern/concrete-component" concreteDecorator "design-pattern-golang-example/decorator-pattern/concrete-decorator"
) func main() { // If you create your account then we will send the notification to your SMS CreateAccount("Tuan Nguyen") // If you update your account, eg: password, name,... then we will send the notification to your SMS, your Facebook and our Slack UpdateAccount("Tuan Nguyen") // If you delete your account then we will send the notification to your SMS, our Slack DeleteAccount("Tuan Nguyen")
} func CreateAccount(name string) { fmt.Println(fmt.Sprintf("Create the account with name %s is done.", name)) time.Sleep(2 * time.Second) fmt.Println("Begin to send the notification...") time.Sleep(2 * time.Second) notifier := &concreteComponent.Notifier{} // add sms notification sms := &concreteDecorator.SmsNotifier{ Notifier: notifier, } // send the notification sms.SendNotification("create account")
} func UpdateAccount(name string) { fmt.Println(fmt.Sprintf("Update the account with name %s is done.", name)) time.Sleep(2 * time.Second) fmt.Println("Begin to send the notification...") time.Sleep(2 * time.Second) notifier := &concreteComponent.Notifier{} // add sms notification sms := &concreteDecorator.SmsNotifier{Notifier: notifier} // add facebook notification facebookSMS := &concreteDecorator.FacebookNotifier{Notifier: sms} // add Slack notification slackFacebookSMS := &concreteDecorator.SlackNotifier{Notifier: facebookSMS} // send the notification slackFacebookSMS.SendNotification("update account")
} func DeleteAccount(name string) { fmt.Println(fmt.Sprintf("Delete the account with name %s is done.", name)) time.Sleep(2 * time.Second) fmt.Println("Begin to send the notification...") time.Sleep(2 * time.Second) notifier := &concreteComponent.Notifier{} // add sms notification sms := &concreteDecorator.SmsNotifier{Notifier: notifier} // add Slack notification slackSMS := &concreteDecorator.SlackNotifier{Notifier: sms} // send the notification slackSMS.SendNotification("delete account")
}
The result:
Create an account
Create the account with name Tuan Nguyen is done.
Begin to send the notification...
Handle process logic common...
Send the notification to SMS with message create account
Done
Update an account
Begin to send the notification...
Handle process logic common...
Send the notification to SMS with message update account
Done
Send the notification to Facebook with message update account
Done
Send the notification to Slack with message update account
Done
Delete an account
Delete the account with name Tuan Nguyen is done.
Begin to send the notification...
Handle process logic common...
Send the notification to SMS with message delete account
Done
Send the notification to Slack with message delete account
Done
III. Source code
- Source code decorator pattern: Example with Golang
- Source almost popular design patterns: Design patterns
- Reference my blog: https://tuannguyenhust.hashnode.dev/design-patterns-decorator-pattern-with-golang