Exploring Behavioral Design Patterns: Strategy & Template Methods
In our last blog, we implemented and discussed creational design patterns. In this blog, we will be exploring behavioral design patterns.
Introduction
Behavioral patterns are used in communication between entities to make communication easier and more flexible such as Iterator, Command, Observer, State, Strategy, Chain of Responsibility, Mediator, and Template Method.
Behavioral design patterns are a type of design pattern that focuses on the communication and interaction between objects in a system. These patterns provide a way to define the communication between objects in a way that makes the system more flexible, reusable, and maintainable. Among the most popular behavioral design patterns are:
We’ll dive into commonly used patterns which are Strategy and Template patterns?
Strategy Pattern
The Strategy pattern is a behavioral design pattern that defines a family of algorithms, objects them, and makes them interchangeable. This pattern enables the runtime selection of an algorithm based on the context or specific requirements.
The following are the key elements of the strategy pattern:
This is the class that requires the use of an algorithm. It keeps a reference to a Strategy object and communicates with it to carry out the algorithm.
Strategy: This is an interface or abstract class that defines a set of methods that are shared by all algorithms. These methods are used by the Context class to communicate with the chosen algorithm.
Concrete Strategies are classes that implement the Strategy interface and implement their algorithms.
Let’s implement this in Go.?
// sum and difference struct for algorithm implementation
type sum struct{}
type difference struct{}
// Wrapper struct with algorithm embedded in it
type WrapperStruct struct {
algorithm IAlgorithm
}
// Implementation of algorithms
func (s sum) ExecuteAlgorithm(a, b int) int {
return a + b
}
func (d difference) ExecuteAlgorithm(a, b int) int {
return a - b
}
// Interface with algorithm algorithm function
type IAlgorithm interface {
ExecuteAlgorithm(a, b int) int
}
// method to embed algo with wrapper struct
func setAlgorithm(algo IAlgorithm) WrapperStruct {
return WrapperStruct{algorithm: algo}
}
And finally, the main method to call the algorithm.
func main() {
s := sum{}
fmt.Println(setAlgorithm(s).algorithm.ExecuteAlgorithm(10, 10))
d := difference{}
fmt.Println(setAlgorithm(d).algorithm.ExecuteAlgorithm(10, 10))
}
So, the output will be 20 and 0.
Template Pattern
The Template Method pattern is a behavioral design pattern in which the skeleton of an algorithm is defined in a method called the Template Method and allows subclasses to redefine specific steps of the algorithm without changing the overall structure.
The Template Method pattern's key components are as follows:
Abstract Class: This is the class that defines the template method and the algorithm's skeleton. It also defines the methods that subclasses will override to provide their implementations.
Concrete Subclasses: These are classes that provide specific implementations of the template method's steps. They also can override other methods defined in the abstract class.
The template method defines the overall structure of the algorithm and calls the methods that the subclasses must implement. This pattern allows the overall structure of the algorithm to be separated from the specifics of the implementation.
Let’s make our hands dirty in the code.
type RegularUser struct {
name string
age int
}
func (r RegularUser) createUser() {
fmt.Sprintf("Regular User Created ! name : %s, age : %d", r.name, r.age)
}
func (p RegularUser) getUserMessage() string {
return fmt.Sprintf("Hi,%s your account is created successfully!", p.name)
}
func (p RegularUser) getAdminMessage() string {
return fmt.Sprintf("New Regular User Registration! Name %s, Age:%d", p.name, p.age)
}
type PremiumUser struct {
name string
age int
}
func (p PremiumUser) createUser() {
fmt.Sprintf("Premium User Created ! name : %s, age : %d", p.name, p.age)
}
func (p PremiumUser) getUserMessage() string {
return fmt.Sprintf(""Hello,%s, your premium account has been successfully created!" %s", p.name)
}
func (p PremiumUser) getAdminMessage() string {
return fmt.Sprintf("New Premium User Registration! Name %s \t Age:%d", p.name, p.age)
}
And the main function will look like this
func sendMessage(message string) {
fmt.Println(message)
}
func main() {
r := RegularUser{name: "John", age: 20}
user := UserCreation{userActivities: r}
user.createAndNotifyUser()
p := RegularUser{name: "Doe", age: 21}
user = UserCreation{userActivities: p}
user.createAndNotifyUser()
}
Conclusion :?
In conclusion, behavioral design patterns are a powerful tool for managing and organizing the behavior of objects in an application. These patterns provide a common vocabulary for discussing and solving common problems related to object interaction and communication. By understanding and applying these patterns, developers can create more flexible, maintainable, and efficient code. Some of the most popular behavioral patterns include the Observer, Mediator, and template method, strategy each of which addresses specific challenges related to object communication and coordination. Overall, the use of behavioral design patterns can greatly improve the design of an application and make it more robust and easier to understand and maintain.