Exploring Behavioral Design Patterns: Strategy & Template Methods

In this blog, we explore behavioral design patterns, with an emphasis on Strategy & Template patterns. The blog highlights the significance of behavioral design patterns in crafting code that is flexible, maintainable & efficient.

GraphQL has a role beyond API Query Language- being the backbone of application Integration
background Coditation

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. 

  • Let’s define a struct with sum and difference and WrapperStruct which holds the algorithm provided and the actual implementation of an algorithm


// 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 function which implements algorithm logic and implementation of an individual is provided above also the function for the embedding algo in wrapper struct.

// 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.

  • We have an interface which has a set of methods which each object needs to implement and also a template method which this method called one by one according to its flow
  • Now we will implement RegularUser and Premium User both should implement the above interface individually 

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)
}

  • PremiumUser concrete implementation

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.

Hello, I am Akshay Navale, a highly driven and passionate software developer, avid learner, and tech enthusiast, always striving to do better. My passion for technology drives me to continuously learn and improve.

Want to receive update about our upcoming podcast?

Thanks for joining our newsletter.
Oops! Something went wrong.

Latest Articles

Implementing feature flags for controlled rollouts and experimentation in production

Discover how feature flags can revolutionize your software deployment strategy in this comprehensive guide. Learn to implement everything from basic toggles to sophisticated experimentation platforms with practical code examples in Java, JavaScript, and Node.js. The post covers essential implementation patterns, best practices for flag management, and real-world architectures that have helped companies like Spotify reduce deployment risks by 80%. Whether you're looking to enable controlled rollouts, A/B testing, or zero-downtime migrations, this guide provides the technical foundation you need to build robust feature flagging systems.

time
12
 min read

Implementing incremental data processing using Databricks Delta Lake's change data feed

Discover how to implement efficient incremental data processing with Databricks Delta Lake's Change Data Feed. This comprehensive guide walks through enabling CDF, reading change data, and building robust processing pipelines that only handle modified data. Learn advanced patterns for schema evolution, large data volumes, and exactly-once processing, plus real-world applications including real-time analytics dashboards and data quality monitoring. Perfect for data engineers looking to optimize resource usage and processing time.

time
12
 min read

Implementing custom embeddings in LlamaIndex for domain-specific information retrieval

Discover how to dramatically improve search relevance in specialized domains by implementing custom embeddings in LlamaIndex. This comprehensive guide walks through four practical approaches—from fine-tuning existing models to creating knowledge-enhanced embeddings—with real-world code examples. Learn how domain-specific embeddings can boost precision by 30-45% compared to general-purpose models, as demonstrated in a legal tech case study where search precision jumped from 67% to 89%.

time
15
 min read