设计模式(Golang)

一、设计原则

软件设计原则是一组指导软件开发人员进行系统设计、模块划分、类和接口定义、代码组织等方面的准则,旨在提高软件的可维护性、可扩展性、灵活性和重用性。以下是软件设计领域中广泛认可的一些核心原则:

1. 开闭原则 (Open-Closed Principle, OCP)

开放(Open):对扩展开放,即允许在不修改现有代码的基础上扩展系统的行为或功能。这意味着应通过扩展而非修改已有类或模块来应对需求变化。

封闭(Closed):对修改封闭,即已有的代码(尤其是稳定的部分)应尽量避免改动。这通常通过抽象和依赖注入来实现,使系统能够在不改动现有代码的情况下添加新功能或适应新情况。

2. 单一职责原则 (Single Responsibility Principle, SRP)

一个类、模块或函数应有且仅有一个引起它变化的原因。换句话说,每个类或模块应专注于一个特定的职责或功能领域,避免承担过多责任。这样有助于保持代码的高内聚性和低耦合性,使得代码更易于理解和测试。

3. 里氏替换原则 (Liskov Substitution Principle, LSP)

子类对象应当能够替换其基类对象出现在任何位置,且不会导致程序的正确性受到影响。这意味着子类必须遵守基类的约定,且不应破坏基类的行为。LSP 强调了继承层次中行为的兼容性,是实现多态和代码复用的重要基础。

4. 依赖倒置原则 (Dependency Inversion Principle, DIP)

高层模块不应依赖于低层模块,两者都应依赖于抽象(接口或抽象类)。具体而言:

  • 高层模块(政策制定者)定义抽象。
  • 低层模块(策略实现者)实现抽象。
  • 抽象不应依赖于细节,细节(具体实现)应依赖于抽象。

DIP 促进了模块间的解耦,使得代码更容易适应变化,同时鼓励面向接口编程。

5. 接口隔离原则 (Interface Segregation Principle, ISP)

客户端不应该被迫依赖它不需要的接口方法。一个大而全的接口应该拆分为多个更小、更专注的接口,每个接口只包含客户端实际需要的方法。ISP 避免了胖接口带来的冗余和耦合,使接口更易于理解和使用。

6. 迪米特法则 (Law of Demeter, LoD) 或最少知识原则 (Least Knowledge Principle)

一个对象应当对其他对象有最少的了解。也就是说,一个对象应尽量减少与其他对象的直接交互,只与"朋友"(直接关联的对象)交谈。迪米特法则有助于减少系统的耦合度,提高模块间的独立性。

7. 合成复用原则 (Composite Reuse Principle, CRP)

优先使用对象组合(has-a 或 contains-a 关系)而不是类继承(is-a 关系)来实现复用。继承在某些场景下是有用的,但它可能导致紧耦合和脆弱的基类。合成则提供了更灵活的结构,允许在运行时动态地改变对象间的协作关系。

应用原则的注意事项

  • 原则之间存在关联:在实际应用中,这些原则往往相互交织,共同指导设计决策。
  • 权衡与折衷:没有绝对的最佳实践,不同原则在特定场景下可能需要权衡取舍。
  • 具体情况具体分析:原则是指导而非教条,应根据项目的具体需求、技术栈、团队习惯等因素灵活运用。

遵循这些软件设计原则,有助于构建出更加健壮、易于维护和扩展的软件系统。设计时应综合考虑这些原则,并结合具体的项目背景和团队共识,做出最适合当前项目的决策。

二、设计模式

创建型模式

工厂模式

目的:提供一个创建对象的统一接口,隐藏对象的具体创建过程。

Go 实现:使用函数或方法返回特定接口类型的实例,根据输入参数或其他条件选择创建不同类型的对象。

Go 复制代码
type Product interface {
    Operation()
}

type ConcreteProductA struct{}
func (p *ConcreteProductA) Operation() {}

type ConcreteProductB struct{}
func (p *ConcreteProductB) Operation() {}

func Factory(productType string) Product {
    switch productType {
    case "A":
        return &ConcreteProductA{}
    case "B":
        return &ConcreteProductB{}
    default:
        panic("Unknown product type")
    }
}

// 使用
product := Factory("A")
product.Operation()
单例模式

目的:确保一个类只有一个实例,并提供一个全局访问点。

Go 实现 :利用 sync.Once 和全局变量确保单例对象只被初始化一次。

Go 复制代码
import "sync"

type Singleton struct{}

var (
    instance *Singleton
    once     sync.Once
)

func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{}
    })
    return instance
}
建造者模式

目的:将复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表示。

Go 实现:定义一个 Builder 接口,实现多个具体的 Builder 类型,客户端通过调用 Builder 的方法逐步构造对象,最后调用 Build 方法得到完整对象。

Go 复制代码
type Vehicle interface {
    Describe() string
}

type CarBuilder struct {
    wheels   int
    doors    int
    color    string
    engine   string
}

func (cb *CarBuilder) SetWheels(wheels int)   { cb.wheels = wheels }
func (cb *CarBuilder) SetDoors(doors int)     { cb.doors = doors }
func (cb *CarBuilder) SetColor(color string)  { cb.color = color }
func (cb *CarBuilder) SetEngine(engine string) { cb.engine = engine }

func (cb *CarBuilder) Build() Vehicle {
    return &Car{
        wheels:   cb.wheels,
        doors:    cb.doors,
        color:    cb.color,
        engine:   cb.engine,
    }
}

type Car struct {
    wheels   int
    doors    int
    color    string
    engine   string
}

func (c *Car) Describe() string {
    return fmt.Sprintf("Car with %d wheels, %d doors, color %s, engine %s",
        c.wheels, c.doors, c.color, c.engine)
}

// 使用
builder := &CarBuilder{}
builder.SetWheels(4).SetColor("Red").SetEngine("V6")
vehicle := builder.Build()
vehicle.Describe()

结构型模式

适配器模式

目的:将一个类的接口转换成客户希望的另一个接口,使得原本不兼容的类可以一起工作。

Go 实现:创建一个新的类型,该类型包含了需要适配的类型,并实现目标接口。

Go 复制代码
type Target interface {
    Request()
}

type Adaptee struct{}

func (a *Adaptee) SpecificRequest() {}

type Adapter struct {
    adaptee *Adaptee
}

func (a *Adapter) Request() {
    a.adaptee.SpecificRequest()
}

// 客户端代码使用 Target 接口,无需知道 Adapter 内部使用了 Adaptee
装饰者模式

目的:动态地给对象添加额外的责任或行为。

Go 实现:通过组合(包含)原对象,创建装饰者对象,并在装饰者中扩展或修改原对象的行为。

Go 复制代码
type Component interface {
    Operation() string
}

type ConcreteComponent struct{}

func (cc *ConcreteComponent) Operation() string {
    return "ConcreteComponent.Operation()"
}

type Decorator struct {
    component Component
}

func (d *Decorator) Operation() string {
    return d.component.Operation()
}

type ConcreteDecoratorA struct {
    Decorator
}

func (cd *ConcreteDecoratorA) Operation() string {
    originalOp := cd.Decorator.Operation()
    return fmt.Sprintf("ConcreteDecoratorA.Operation() -> %s", originalOp)
}

// 使用
component := &ConcreteComponent{}
decorated := &ConcreteDecoratorA{Decorator: Decorator{component: component}}
fmt.Println(decorated.Operation())

行为型模式

观察者模式

目的:定义对象间一对多的依赖关系,当一个对象状态改变时,所有依赖于它的对象都会收到通知并自动更新。

Go 实现 :使用 channels 和 goroutines 实现异步通知,或利用 sync.Cond 实现同步通知。

Go 复制代码
type Subject interface {
    Register(Observer)
    Unregister(Observer)
    NotifyObservers()
}

type ConcreteSubject struct {
    observers []Observer
    state     string
}

func (s *ConcreteSubject) Register(o Observer) {
    s.observers = append(s.observers, o)
}

func (s *ConcreteSubject) Unregister(o Observer) {
    // ...
}

func (s *ConcreteSubject) NotifyObservers() {
    for _, observer := range s.observers {
        go observer.Update(s.state)
    }
}

type Observer interface {
    Update(state string)
}

// 实现 Observer 接口的具体观察者
策略模式

目的:定义一系列算法,并将每个算法封装为一个单独的类,使得算法可以在运行时进行切换。

Go 实现:定义一个接口(策略),实现多个具体的策略类型,客户端根据需要选择并传递合适的策略对象。

Go 复制代码
type Strategy interface {
    Calculate(numbers []int) int
}

type AddStrategy struct{}

func (as *AddStrategy) Calculate(numbers []int) int {
    sum := 0
    for _, n := range numbers {
        sum += n
    }
    return sum
}

type MultiplyStrategy struct{}

func (ms *MultiplyStrategy) Calculate(numbers []int) int {
    product := 1
    for _, n := range numbers {
        product *= n
    }
    return product
}

func Process(numbers []int, strategy Strategy) int {
    return strategy.Calculate(numbers)
}

// 使用
result := Process([]int{1, 2, 3}, &AddStrategy{})
fmt.Println(result)  // 输出 6

result = Process([]int{1, 2, 3}, &MultiplyStrategy{})
fmt.Println(result)  // 输出 6

其他模式

除了上述示例,Go 语言中还可以实现诸如模板方法模式、命令模式、迭代器模式、中介者模式、备忘录模式、解释器模式、状态模式、访问者模式、责任链模式等。在应用设计模式时,应遵循设计原则,结合 Go 语言的特性(如接口、并发模型等),并根据具体需求进行调整和创新。

相关推荐
晨米酱6 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机11 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机12 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机12 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机12 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤12 小时前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴1 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
李广坤2 天前
工厂模式
设计模式