Menu
Coddy logo textTech

Intro to Design Patterns

Part of the Object Oriented Programming section of Coddy's GO journey — lesson 85 of 107.

Design patterns are reusable solutions to common problems that arise in software design. They aren't code you copy directly, but rather templates that guide how you structure your types and their interactions to solve specific challenges.

Design patterns are typically grouped into three categories:

CategoryPurposeExamples
CreationalObject creation mechanismsSingleton, Factory, Builder
StructuralComposing types into larger structuresAdapter, Decorator, Composite
BehavioralCommunication between objectsObserver, Strategy, Command

In Go, design patterns look different than in traditional OOP languages.

Without classes or inheritance, Go implements patterns using interfaces, struct embedding, and composition. For example, where Java might use an abstract class, Go uses an interface. Where C++ might use inheritance, Go uses embedding.

Learning patterns helps you recognize common problems and communicate solutions effectively with other developers. When someone mentions "use the Strategy pattern here," the entire team understands the approach without lengthy explanations.

Over the next lessons, you'll implement several fundamental patterns in Go, starting with creational patterns like Singleton and Factory, then moving to behavioral patterns like Observer and Strategy.

challenge icon

Challenge

Easy

Let's build a simple notification system that demonstrates how design patterns help organize code! You'll create a structure that could easily be extended with patterns like Observer or Strategy in future lessons—but for now, focus on setting up clean interfaces and types that follow good OOP principles.

You'll organize your code across three files:

  • notifier.go: Define the core interface and types for your notification system.

    Create a Notifier interface with a single method Send(message string) string that returns a confirmation message.

    Implement two concrete notifier types:

    • EmailNotifier with an Address field—its Send method returns Email to [address]: [message]
    • SMSNotifier with a PhoneNumber field—its Send method returns SMS to [phone]: [message]
  • dispatcher.go: Create a dispatcher that works with any notifier through the interface.

    Build a NotificationDispatcher struct that holds a slice of Notifier interfaces. Add these methods:

    • AddNotifier(n Notifier) to register a notifier
    • Broadcast(message string) []string that sends the message through all registered notifiers and returns a slice of all confirmation strings

    Also create a NewNotificationDispatcher() constructor that returns an initialized dispatcher.

  • main.go: Set up notifiers and broadcast messages.

    Read a count of notifiers to create. For each notifier, read its type (email or sms) and its destination (address or phone number). Add each to a dispatcher.

    Then read a message to broadcast. Send it through all notifiers and print each confirmation on its own line.

The following inputs will be provided:

  • Line 1: Number of notifiers
  • For each notifier: type (email or sms), then destination
  • Final line: Message to broadcast

For example, given:

3
email
alice@example.com
sms
555-1234
email
bob@example.com
Server maintenance at midnight

Your output should be:

Email to alice@example.com: Server maintenance at midnight
SMS to 555-1234: Server maintenance at midnight
Email to bob@example.com: Server maintenance at midnight

And given:

2
sms
555-9999
sms
555-0000
Meeting canceled

Your output should be:

SMS to 555-9999: Meeting canceled
SMS to 555-0000: Meeting canceled

This structure demonstrates how interfaces enable flexible, extensible designs. The dispatcher doesn't know about specific notifier types—it just knows they can Send. This is the foundation that patterns like Observer and Strategy build upon!

Cheat sheet

Design patterns are reusable solutions to common problems in software design. They are templates that guide how to structure types and their interactions.

Design patterns are grouped into three categories:

CategoryPurposeExamples
CreationalObject creation mechanismsSingleton, Factory, Builder
StructuralComposing types into larger structuresAdapter, Decorator, Composite
BehavioralCommunication between objectsObserver, Strategy, Command

In Go, design patterns are implemented using interfaces, struct embedding, and composition instead of classes and inheritance. Where traditional OOP languages use abstract classes, Go uses interfaces. Where they use inheritance, Go uses embedding.

Try it yourself

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Read the number of notifiers
	countStr, _ := reader.ReadString('\n')
	count, _ := strconv.Atoi(strings.TrimSpace(countStr))

	// Create a new dispatcher
	dispatcher := NewNotificationDispatcher()

	// Read each notifier and add to dispatcher
	for i := 0; i < count; i++ {
		notifierType, _ := reader.ReadString('\n')
		notifierType = strings.TrimSpace(notifierType)

		destination, _ := reader.ReadString('\n')
		destination = strings.TrimSpace(destination)

		// TODO: Create the appropriate notifier based on type
		// and add it to the dispatcher
		// Hint: Check if notifierType is "email" or "sms"

	}

	// Read the message to broadcast
	message, _ := reader.ReadString('\n')
	message = strings.TrimSpace(message)

	// TODO: Broadcast the message and print each confirmation

}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming