Effective Design Patterns in Go

Leapcell: The Best of Serverless Web Hosting Implementation of Ten Design Patterns in Go Language and Their Applications in Internet Scenarios 1. Singleton Pattern Pattern Definition Ensure that only one instance of a class is created globally, and provide a unified access point. It belongs to the creational pattern and is suitable for scenarios where global unique control is required. Core Features Unique Instance: There is only one object instance globally. Self-initialization: The class itself is responsible for creating the instance. Global Access: Provide an access entry through a static method or a global variable. Advantages and Disadvantages Advantages Disadvantages Ensure global uniqueness of resources/state Violate the principle of high cohesion, with high module coupling Reduce repeated creation of resources Difficult to isolate unit tests Provide a convenient global access point Damage the dependency injection pattern Scenario Applications Microservice Configuration Center (such as an alternative to Spring Cloud Config): Manage the configuration of distributed systems uniformly. Database Connection Pool (such as connection management for PostgreSQL/MySQL): Control the number of concurrent connections. Distributed Log Service (such as the log collector in the ELK Stack): Avoid repeated creation of log handlers. API Rate Limiter (such as the request frequency control of the Stripe API): Share the rate limiting state globally. Go Language Implementation (Concurrency-safe Version) package singleton import ( "sync" ) // ConfigManager Singleton struct, simulating configuration management type ConfigManager struct { AppConfig map[string]string } var ( once sync.Once instance *ConfigManager ) // GetInstance Global access point, using sync.Once to ensure concurrency safety func GetInstance() *ConfigManager { once.Do(func() { instance = &ConfigManager{ AppConfig: map[string]string{ "env": "prod", "port": "8080", "timeout": "30s", }, } }) return instance } // Concurrency test example func TestConcurrency() { var wg sync.WaitGroup for i := 0; i f.offset { return amount - f.offset } return amount } // CheckoutContext Checkout context type CheckoutContext struct { strategy PromotionStrategy } func (c *CheckoutContext) SetStrategy(strategy PromotionStrategy) { c.strategy = strategy } func (c *CheckoutContext) CalculateFinalAmount(amount float64) float64 { return c.strategy.Calculate(amount) } 6. Adapter Pattern Pattern Definition Convert incompatible interfaces so that classes with different interfaces can work together. It belongs to the structural pattern and is suitable for system integration scenarios. Core Features Interface Conversion: Adapt the source interface to the target interface. Decouple the System: The client and the adaptee do not need to interact directly. Compatibility: Retain the original system code and add an adapter layer. Advantages and Disadvantages Advantages Disadvantages Solve the problem of interface incompatibility Increase the complexity of the system's abstract layer Reuse legacy system code Excessive use may make the system difficult to maintain Support two-way adaptation May introduce performance overhead Scenario Applications Legacy System Migration (Docking a COBOL system with a microservice architecture): Adapt the old system API. Third-party Library Integration (Adapt the MongoDB driver to a SQL interface): Unify the data access layer. Cross-platform Development (Adapt iOS/Android native APIs to a unified interface): Mobile application framework. Cloud Service Docking (Adapt AWS SQS to the Kafka message format): Hybrid cloud architecture. Go Language Implementation (Third-party Payment Adaptation) package adapter import ( "errors" "fmt" ) // ThirdPartyPayment Third-party payment interface (source interface) type ThirdPartyPayment interface { ProcessPayment(orderID string, amount float64) error } // LegacyPayPal Legacy PayPal implementation (source implementation) type LegacyPayPal struct{} func (p *LegacyPayPal) ProcessPayment(orderID string, amount float64) error { fmt.Printf("Legacy PayPal processing order %s for $%.2f\n", orderID, amount) return nil } // TargetPayment Target payment interface (unified interface) type TargetPayment interface { Pay(orderID string, amountUSD float64) error } // PayPalAdapter Adapter implementation type PayPalAdapter struct { legacy *LegacyPayPal } func (a *PayPalAdapter) Pay(orderID string, amountUSD float64) error { // Convert the target interface to the source interface parameters return a.legacy.ProcessPayment(order

Apr 19, 2025 - 17:16
 0
Effective Design Patterns in Go

Image description

Leapcell: The Best of Serverless Web Hosting

Implementation of Ten Design Patterns in Go Language and Their Applications in Internet Scenarios

1. Singleton Pattern

Pattern Definition

Ensure that only one instance of a class is created globally, and provide a unified access point. It belongs to the creational pattern and is suitable for scenarios where global unique control is required.

Core Features

  • Unique Instance: There is only one object instance globally.
  • Self-initialization: The class itself is responsible for creating the instance.
  • Global Access: Provide an access entry through a static method or a global variable.

Advantages and Disadvantages

Advantages Disadvantages
Ensure global uniqueness of resources/state Violate the principle of high cohesion, with high module coupling
Reduce repeated creation of resources Difficult to isolate unit tests
Provide a convenient global access point Damage the dependency injection pattern

Scenario Applications

  1. Microservice Configuration Center (such as an alternative to Spring Cloud Config): Manage the configuration of distributed systems uniformly.
  2. Database Connection Pool (such as connection management for PostgreSQL/MySQL): Control the number of concurrent connections.
  3. Distributed Log Service (such as the log collector in the ELK Stack): Avoid repeated creation of log handlers.
  4. API Rate Limiter (such as the request frequency control of the Stripe API): Share the rate limiting state globally.

Go Language Implementation (Concurrency-safe Version)

package singleton

import (
    "sync"
)

// ConfigManager Singleton struct, simulating configuration management
type ConfigManager struct {
    AppConfig map[string]string
}

var (
    once     sync.Once
    instance *ConfigManager
)

// GetInstance Global access point, using sync.Once to ensure concurrency safety
func GetInstance() *ConfigManager {
    once.Do(func() {
        instance = &ConfigManager{
            AppConfig: map[string]string{
                "env":     "prod",
                "port":    "8080",
                "timeout": "30s",
            },
        }
    })
    return instance
}

// Concurrency test example
func TestConcurrency() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            config := GetInstance()
            println(config.AppConfig["env"])
        }()
    }
    wg.Wait()
}

2. Factory Pattern

Pattern Definition

Encapsulate the object creation logic, and determine the specific product to be instantiated through subclasses. It belongs to the creational pattern and decouples the object creation from its usage.

Core Features

  • Encapsulation of Creation Logic: The client does not need to know the specific creation details.
  • Polymorphism Support: Extend the product family through interfaces.
  • Open-Closed Principle: Adding new products does not require modifying the existing code.

Advantages and Disadvantages

Advantages Disadvantages
Separate object creation from usage Increase the number of classes (each product needs a corresponding factory)
Easy to extend new products The factory class may become too complex
Comply with the Dependency Inversion Principle The client needs to know the abstract interface of the product

Scenario Applications

  1. Payment Gateway Integration (Stripe/PayPal/Apple Pay): Create different processors according to the payment method.
  2. Cloud Storage Client (S3/GCS/Azure Blob): Create corresponding API clients according to the storage service.
  3. Message Queue Adapter (Kafka/RabbitMQ/NATS): Unify the creation logic of the message sending interface.
  4. Third-party Login Service (OAuth2/OpenID Connect): Dynamically generate authentication clients for different platforms.

Go Language Implementation (Abstract Factory Pattern)

package factory

import "fmt"

// PaymentProcessor Payment processor interface
type PaymentProcessor interface {
    Process(amount float64) string
}

// StripeProcessor Stripe payment implementation
type StripeProcessor struct{}
func (s *StripeProcessor) Process(amount float64) string {
    return fmt.Sprintf("Stripe processed $%.2f", amount)
}

// PayPalProcessor PayPal payment implementation
type PayPalProcessor struct{}
func (p *PayPalProcessor) Process(amount float64) string {
    return fmt.Sprintf("PayPal processed $%.2f", amount)
}

// PaymentFactory Factory interface
type PaymentFactory interface {
    CreateProcessor() PaymentProcessor
}

// StripeFactory Stripe factory implementation
type StripeFactory struct{}
func (s *StripeFactory) CreateProcessor() PaymentProcessor {
    return &StripeProcessor{}
}

// PayPalFactory PayPal factory implementation
type PayPalFactory struct{}
func (p *PayPalFactory) CreateProcessor() PaymentProcessor {
    return &PayPalProcessor{}
}

// Client usage example
func NewPaymentClient(factory PaymentFactory) PaymentProcessor {
    return factory.CreateProcessor()
}

3. Observer Pattern

Pattern Definition

Define a one-to-many dependency relationship between objects, and automatically notify all observers when the state of the subject changes. It belongs to the behavioral pattern and is suitable for event-driven scenarios.

Core Features

  • Event-driven: The change of the subject's state triggers the update of the observers.
  • Loose Coupling: The subject and the observers only interact through abstract interfaces.
  • Dynamic Subscription: Observers can register or unregister at any time.

Advantages and Disadvantages

Advantages Disadvantages
Support broadcast-style event notification A large number of observers may lead to performance problems
Easy to expand new observer types Circular dependencies may cause memory leaks
Comply with the Open-Closed Principle The subject needs to maintain a list of observers

Scenario Applications

  1. Real-time Notification System (Slack channel updates/Zoom meeting reminders): Users subscribe to the subject to receive real-time notifications.
  2. Stock Quotation Platform (Robinhood/Nasdaq real-time quotes): Investors subscribe to stock codes to get price changes.
  3. E-commerce Inventory Monitoring (Amazon product restocking notifications): Users subscribe to products to receive inventory change reminders.
  4. Distributed System Monitoring (Datadog metric alarms): The monitoring service subscribes to the state changes of microservices.

Go Language Implementation (Event-driven Model)

package observer

import "fmt"

// Observer Observer interface
type Observer interface {
    Update(message string)
}

// Subject Subject interface
type Subject interface {
    Attach(observer Observer)
    Detach(observer Observer)
    Notify(message string)
}

// StockTicker Specific subject: stock quotation
type StockTicker struct {
    observers []Observer
    symbol    string
    price     float64
}

func (s *StockTicker) Attach(observer Observer) {
    s.observers = append(s.observers, observer)
}

func (s *StockTicker) Detach(observer Observer) {
    for i, o := range s.observers {
        if o == observer {
            s.observers = append(s.observers[:i], s.observers[i+1:]...)
            return
        }
    }
}

func (s *StockTicker) Notify(message string) {
    for _, o := range s.observers {
        o.Update(message)
    }
}

// Investor Specific observer: investor
type Investor struct {
    name string
}

func (i *Investor) Update(message string) {
    fmt.Printf("%s received: %s\n", i.name, message)
}

4. Decorator Pattern

Pattern Definition

Dynamically add new functions to an object, and extend its responsibilities through composition rather than inheritance. It belongs to the structural pattern and is suitable for scenarios with functional layering.

Core Features

  • Transparent Extension: Keep the interface consistent, and the client is unaware of it.
  • Composition Feature: Support nesting of multiple layers of decorators.
  • Runtime Binding: Dynamically determine the combination of object functions.

Advantages and Disadvantages

Advantages Disadvantages
Avoid inheritance hierarchy explosion Debugging may be difficult with multiple layers of decorators
Support free combination of functions Excessive use will increase the complexity of the system
Comply with the Open-Closed Principle The order of decoration may affect the execution result

Scenario Applications

  1. API Middleware (Express.js/Koa.js middleware system): Stack functions such as logging, authentication, and rate limiting.
  2. E-commerce Order Processing (shipping fee calculation/discount offer/tax processing): Calculate the total order amount step by step.
  3. Cloud Storage Service (S3 encryption/compression/CDN acceleration): Dynamically combine storage functions.
  4. Microservice Gateway (Kong/NginX plugin system): Request verification, response transformation, and traffic monitoring.

Go Language Implementation (HTTP Middleware Simulation)

package decorator

import "fmt"

// Handler Basic processing function
type Handler func(request string) string

// LogDecorator Log decorator
func LogDecorator(next Handler) Handler {
    return func(request string) string {
        fmt.Printf("Received request: %s\n", request)
        return next(request)
    }
}

// AuthDecorator Authentication decorator
func AuthDecorator(next Handler) Handler {
    return func(request string) string {
        if request != "authenticated" {
            return "Unauthorized"
        }
        return next(request)
    }
}

// Basic processing function
func BasicHandler(request string) string {
    return fmt.Sprintf("Processed: %s", request)
}

// Composite decorator example
func CompositeHandler() Handler {
    return LogDecorator(AuthDecorator(BasicHandler))
}

5. Strategy Pattern

Pattern Definition

Encapsulate algorithms into independent strategy classes, and support dynamic switching at runtime. It belongs to the behavioral pattern and is suitable for scenarios with multiple algorithms.

Core Features

  • Algorithm Encapsulation: Each strategy independently implements the algorithm.
  • Strategy Switching: Dynamically select a strategy at runtime.
  • Open-Closed Principle: Adding a new strategy does not require modifying the existing code.

Advantages and Disadvantages

Advantages Disadvantages
Decouple the algorithm from the client The client needs to know all strategy types
Facilitate algorithm extension and replacement The number of strategy classes may grow rapidly
Support combined strategies It is difficult to share the state between strategies

Scenario Applications

  1. E-commerce Promotion Engine (full reduction/discount/gift strategies): Dynamically switch the calculation logic according to the activity type.
  2. Logistics Distribution System (FedEx/UPS/USPS shipping strategies): Calculate the shipping fee according to the user's choice.
  3. Data Sorting Service (sort by price/sales volume/rating): The front-end request determines the sorting strategy.
  4. Advertisement Placement Algorithm (precision marketing/geographical targeting/frequency control): Adjust the placement strategy in real time.

Go Language Implementation (E-commerce Promotion Calculation)

package strategy

import "fmt"

// PromotionStrategy Promotion strategy interface
type PromotionStrategy interface {
    Calculate(amount float64) float64
}

// PercentDiscount Percentage discount strategy
type PercentDiscount struct {
    rate float64 // Discount rate (0.1 = 10%)
}

func (p *PercentDiscount) Calculate(amount float64) float64 {
    return amount * (1 - p.rate)
}

// FixedDiscount Fixed amount discount strategy
type FixedDiscount struct {
    offset float64 // Fixed reduction amount
}

func (f *FixedDiscount) Calculate(amount float64) float64 {
    if amount > f.offset {
        return amount - f.offset
    }
    return amount
}

// CheckoutContext Checkout context
type CheckoutContext struct {
    strategy PromotionStrategy
}

func (c *CheckoutContext) SetStrategy(strategy PromotionStrategy) {
    c.strategy = strategy
}

func (c *CheckoutContext) CalculateFinalAmount(amount float64) float64 {
    return c.strategy.Calculate(amount)
}

6. Adapter Pattern

Pattern Definition

Convert incompatible interfaces so that classes with different interfaces can work together. It belongs to the structural pattern and is suitable for system integration scenarios.

Core Features

  • Interface Conversion: Adapt the source interface to the target interface.
  • Decouple the System: The client and the adaptee do not need to interact directly.
  • Compatibility: Retain the original system code and add an adapter layer.

Advantages and Disadvantages

Advantages Disadvantages
Solve the problem of interface incompatibility Increase the complexity of the system's abstract layer
Reuse legacy system code Excessive use may make the system difficult to maintain
Support two-way adaptation May introduce performance overhead

Scenario Applications

  1. Legacy System Migration (Docking a COBOL system with a microservice architecture): Adapt the old system API.
  2. Third-party Library Integration (Adapt the MongoDB driver to a SQL interface): Unify the data access layer.
  3. Cross-platform Development (Adapt iOS/Android native APIs to a unified interface): Mobile application framework.
  4. Cloud Service Docking (Adapt AWS SQS to the Kafka message format): Hybrid cloud architecture.

Go Language Implementation (Third-party Payment Adaptation)

package adapter

import (
    "errors"
    "fmt"
)

// ThirdPartyPayment Third-party payment interface (source interface)
type ThirdPartyPayment interface {
    ProcessPayment(orderID string, amount float64) error
}

// LegacyPayPal Legacy PayPal implementation (source implementation)
type LegacyPayPal struct{}
func (p *LegacyPayPal) ProcessPayment(orderID string, amount float64) error {
    fmt.Printf("Legacy PayPal processing order %s for $%.2f\n", orderID, amount)
    return nil
}

// TargetPayment Target payment interface (unified interface)
type TargetPayment interface {
    Pay(orderID string, amountUSD float64) error
}

// PayPalAdapter Adapter implementation
type PayPalAdapter struct {
    legacy *LegacyPayPal
}

func (a *PayPalAdapter) Pay(orderID string, amountUSD float64) error {
    // Convert the target interface to the source interface parameters
    return a.legacy.ProcessPayment(orderID, amountUSD)
}

// Example of handling incompatible situations
func HandleIncompatiblePayment(payment ThirdPartyPayment) TargetPayment {
    return &PayPalAdapter{legacy: payment.(*LegacyPayPal)}
}

7. Proxy Pattern

Pattern Definition

Provide a proxy layer for the target object to control access to it. It belongs to the structural pattern and is suitable for scenarios such as remote access and permission control.

Core Features

  • Access Control: The proxy layer intercepts and processes requests.
  • Lazy Loading: Create the target object on demand.
  • Separation of Responsibilities: The proxy handles non-business logic (logging/security/caching).

Advantages and Disadvantages

Advantages Disadvantages
Protect the target object Increase the request processing delay
Support distributed access The proxy layer may become a single point of failure
Implement lazy loading The interfaces of the proxy class and the target class need to be consistent

Scenario Applications

  1. API Gateway (Apigee/Postman API management): Proxy backend microservices and provide a unified entry.
  2. Cache Proxy (Redis/Memcached cache layer): Cache database query results.
  3. Permission Proxy (OAuth2 authorization server): Intercept requests and verify user permissions.
  4. Remote Proxy (gRPC remote service call): Hide the details of network communication.

Go Language Implementation (Cache Proxy)

package proxy

import (
    "sync"
    "time"
)

// RealData Real data object (database query)
type RealData struct {
    ID       string
    Content  string
    LastUpdate time.Time
}

func (r *RealData) FetchData() string {
    // Simulate database query delay
    time.Sleep(500 * time.Millisecond)
    return r.Content
}

// CacheProxy Cache proxy
type CacheProxy struct {
    realData *RealData
    cache    map[string]string
    mu       sync.Mutex
}

func NewCacheProxy(id string, content string) *CacheProxy {
    return &CacheProxy{
        realData: &RealData{ID: id, Content: content, LastUpdate: time.Now()},
        cache:    make(map[string]string),
    }
}

func (c *CacheProxy) FetchData() string {
    c.mu.Lock()
    defer c.mu.Unlock()

    if data, exists := c.cache[c.realData.ID]; exists {
        return data // Return the cached data directly
    }

    // First access, get the real data and cache it
    data := c.realData.FetchData()
    c.cache[c.realData.ID] = data
    return data
}

8. Command Pattern

Pattern Definition

Encapsulate requests into command objects to decouple the request sender from the receiver. It belongs to the behavioral pattern and is suitable for scenarios that support undo and logging.

Core Features

  • Request Encapsulation: The command object contains all the information required for execution.
  • Transaction Support: Support batch execution and rollback.
  • Logging: The execution history of commands can be persisted.

Advantages and Disadvantages

Advantages Disadvantages
Support undo/redo operations The number of command classes may increase significantly
Decouple request sending and receiving Complex command logic is difficult to maintain
Support batch processing Command parameters need to be determined in advance

Scenario Applications

  1. Version Control System (Git commit operations): Each commit can be treated as a command that supports rollback.
  2. Database Transaction (Encapsulation of ACID operations): Combine multiple SQL commands to support commit/rollback.
  3. Task Scheduling System (Airflow task orchestration): Encapsulate distributed tasks into executable commands.
  4. Text Editor (Undo/Redo functions): Record editing operations as command objects.

Go Language Implementation (Database Transaction Simulation)

package command

import (
    "database/sql"
    "fmt"
)

// DatabaseReceiver Database receiver
type DatabaseReceiver struct {
    db *sql.DB
}

func (r *DatabaseReceiver) ExecuteSQL(sqlStmt string) error {
    fmt.Printf("Executing: %s\n", sqlStmt)
    return nil // Simulate SQL execution
}

func (r *DatabaseReceiver) Rollback() error {
    fmt.Println("Rolling back transaction")
    return nil // Simulate rollback
}

// Command Command interface
type Command interface {
    Execute() error
    Undo() error
}

// InsertCommand Insert command
type InsertCommand struct {
    receiver   *DatabaseReceiver
    table      string
    columns    []string
    values     []string
    prevValues map[string]string // Store old values for rollback
}

func (c *InsertCommand) Execute() error {
    // Execute the insert logic and record the old values
    c.prevValues = getPreviousValues(c.receiver, c.table, c.columns)
    return c.receiver.ExecuteSQL(fmt.Sprintf("INSERT INTO %s VALUES (%s)", c.table, c.values))
}

func (c *InsertCommand) Undo() error {
    // Use prevValues to roll back the insert operation
    return c.receiver.Rollback()
}

9. Composite Pattern

Pattern Definition

Combine objects into a tree structure and uniformly process individual objects (leaves) and composite objects (containers). It belongs to the structural pattern and is suitable for hierarchical management scenarios.

Core Features

  • Tree Structure: Support part-whole hierarchical relationships.
  • Unified Interface: The client treats leaves and containers consistently.
  • Recursive Processing: Container objects can contain other containers or leaves.

Advantages and Disadvantages

Advantages Disadvantages
Simplify hierarchical structure operations Higher design complexity
Support complex hierarchical traversal The interfaces of leaves and containers need to be strictly unified
Comply with the Open-Closed Principle Function expansion may affect the overall structure

Scenario Applications

  1. File System (AWS S3's Bucket/Object structure): Process files and folders uniformly.
  2. Organization Structure (Department/Employee management in an enterprise HR system): A department can contain sub-departments and employees.
  3. E-commerce Product Catalog (Amazon's category/subcategory/product hierarchy): Hierarchical product management.
  4. GUI Components (View/Text component nesting in React Native): Process the UI component tree uniformly.

Go Language Implementation (File System Simulation)

package composite

import "fmt"

// FileSystemNode File system node interface
type FileSystemNode interface {
    List() string
    Add(node FileSystemNode)
    Remove(node FileSystemNode)
}

// File Leaf node: file
type File struct {
    name string
    size int
}

func (f *File) List() string {
    return fmt.Sprintf("File: %s (%dKB)", f.name, f.size)
}

func (f *File) Add(node FileSystemNode)   {}
func (f *File) Remove(node FileSystemNode) {}

// Directory Container node: directory
type Directory struct {
    name    string
    children []FileSystemNode
}

func (d *Directory) List() string {
    list := fmt.Sprintf("Directory: %s\n", d.name)
    for _, child := range d.children {
        list += fmt.Sprintf("  ├─ %s\n", child.List())
    }
    return list
}

func (d *Directory) Add(node FileSystemNode) {
    d.children = append(d.children, node)
}

func (d *Directory) Remove(node FileSystemNode) {
    for i, child := range d.children {
        if child == node {
            d.children = append(d.children[:i], d.children[i+1:]...)
            return
        }
    }
}

10. Iterator Pattern

Pattern Definition

Provide a unified interface to traverse the elements of a collection and hide the internal data structure of the collection. It belongs to the behavioral pattern and is suitable for data traversal scenarios.

Core Features

  • Abstract Traversal: The client does not need to know the internal implementation of the collection.
  • Multiple Traversal Modes: Support traversal strategies such as forward, reverse, and filtering.
  • Decouple the Collection: The collection and the iterator can evolve independently.

Advantages and Disadvantages

Advantages Disadvantages
Unify the collection access interface Increase the design cost of the iterator class
Support traversal of complex data structures Customizing iterators may lead to class bloat
Comply with the Single Responsibility Principle The iterator needs to maintain the traversal state

Scenario Applications

  1. Big Data Processing (Traversal of data sets in Hadoop MapReduce): Process different storage formats uniformly.
  2. Database ORM (Traversal of result sets in GORM): Transparently handle the return formats of different databases.
  3. Graphic Rendering (Traversal of scene graph nodes in Three.js): Process the hierarchical structure of 3D objects uniformly.
  4. Log Analysis (Traversal of log entries in ELK): Support traversal of different log storage formats.

Go Language Implementation (Custom Collection Iterator)

package iterator

import "fmt"

// Collection Collection interface
type Collection interface {
    CreateIterator() Iterator
    AddItem(item string)
}

// StringCollection String collection implementation
type StringCollection struct {
    items []string
}

func (c *StringCollection) CreateIterator() Iterator {
    return &StringIterator{collection: c, index: -1}
}

func (c *StringCollection) AddItem(item string) {
    c.items = append(c.items, item)
}

// Iterator Iterator interface
type Iterator interface {
    Next() bool
    Current() string
}

// StringIterator String iterator implementation
type StringIterator struct {
    collection *StringCollection
    index      int
}

func (i *StringIterator) Next() bool {
    i.index++
    return i.index < len(i.collection.items)
}

func (i *StringIterator) Current() string {
    if i.index < len(i.collection.items) {
        return i.collection.items[i.index]
    }
    return ""
}

// Usage example
func TraverseCollection(collection Collection) {
    iterator := collection.CreateIterator()
    for iterator.Next() {
        fmt.Println(iterator.Current())
    }
}

Summary

The Go language provides flexible support for the implementation of design patterns through its interface and composition features:

  1. Creational Patterns: Use the singleton pattern to control the global state, and the factory pattern to encapsulate object creation.
  2. Structural Patterns: Achieve the functional extension of decorators, proxies, and adapters through composition.
  3. Behavioral Patterns: Decouple and implement the interaction logic of observers, strategies, and commands with the help of interfaces.

In actual development, an appropriate pattern should be selected according to specific scenarios:

  • For microservice architectures, the factory pattern and the adapter pattern are preferred.
  • For event-driven systems, the observer pattern and the command pattern are recommended.
  • For hierarchical data management, the composite pattern and the iterator pattern are the first choices.

The reasonable use of design patterns can improve the maintainability, extensibility, and reusability of the code. However, over-design should be avoided, and the focus should always be on solving practical problems.

Leapcell: The Best of Serverless Web Hosting

Finally, I would like to recommend a platform Leapcell that is most suitable for deploying Go services.

Image description