设计模式(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 实现:使用函数或方法返回特定接口类型的实例,根据输入参数或其他条件选择创建不同类型的对象。

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 和全局变量确保单例对象只被初始化一次。

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 方法得到完整对象。

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 实现:创建一个新的类型,该类型包含了需要适配的类型,并实现目标接口。

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 实现:通过组合(包含)原对象,创建装饰者对象,并在装饰者中扩展或修改原对象的行为。

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 实现同步通知。

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

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 语言的特性(如接口、并发模型等),并根据具体需求进行调整和创新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/549419.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

windows下已经创建好了虚拟环境,但是切换不了的解决方法

用得多Ubuntu,今天用Windows重新更新anaconda出问题,重新安装之后,打开pycharm发现打开终端之后,刚开始是ps的状态,后面试了网上改cmd的方法,终端变成c盘开头了 切换到虚拟环境如下:目前的shell…

ON1 NoNoise AI 2024 for Mac/Win:智能降噪,重塑影像之美

在数字摄影领域,图片质量往往受到多种因素的影响,其中噪点问题尤为突出。ON1 NoNoise AI 2024作为一款专为Mac和Windows平台打造的AI图片降噪工具,凭借其卓越的降噪性能和智能化的操作体验,成为了摄影师和图像处理专家们的首选工具…

NL2SQL进阶系列(5):论文解读业界前沿方案(DIN-SQL、C3-SQL、DAIL-SQL、SQL-PaLM)、新一代数据集BIRD-SQL解读

NL2SQL进阶系列(5):论文解读业界前沿方案(DIN-SQL、C3-SQL、DAIL-SQL)、新一代数据集BIRD-SQL解读 NL2SQL基础系列(1):业界顶尖排行榜、权威测评数据集及LLM大模型(Spider vs BIRD)全面对比优劣分析[Text2…

基于Springboot+Vue的Java项目-免税商品优选购物商城系统开发实战(附演示视频+源码+LW)

大家好!我是程序员一帆,感谢您阅读本文,欢迎一键三连哦。 💞当前专栏:Java毕业设计 精彩专栏推荐👇🏻👇🏻👇🏻 🎀 Python毕业设计 &am…

故障转移-redis

4.4.故障转移 集群初识状态是这样的: 其中7001、7002、7003都是master,我们计划让7002宕机。 4.4.1.自动故障转移 当集群中有一个master宕机会发生什么呢? 直接停止一个redis实例,例如7002: redis-cli -p 7002 sh…

Linux环境变量(一)

一.main参数 如果你仔细看过编程书籍就会发现,对于主函数main函数也是有参数的: 首先,我们先来认识两个参数: int main(int argc,char* argv[]) {return 0; } 对于这两个参数:第一个参数int类型表示为第二个的个数…

[C++][算法基础]判定二分图(染色法)

给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环。 请你判断这个图是否是二分图。 输入格式 第一行包含两个整数 n 和 m。 接下来 m 行,每行包含两个整数 u 和 v,表示点 u 和点 v 之间存在一条边。 输出格式 如果给定图是二分图…

字体反爬知识积累2

一、os模块中函数的应用 如何获取当前文件中所有文件的路径方法 这段代码使用 os.walk()函数来遍历指定目录 imgs 下的所有子目录和文件。具体来说,os.walk()函数返回一个生成器,可以在每次迭代中获取目录树中的一个元组,元组包含当前目录的…

【算法】删除链表中重复元素

本题来源---《删除链表中重复元素》。 题目描述 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回已排序的链表 。 示例 1: 输入:head [1,1,2] 输出:[1,2]示例 2: 输入…

Delphi Xe 10.3 钉钉SDK开发——审批流接口(获取表单ProcessCode)

开发钉钉审批流时,需要用到钉钉表单的Processcode,有两种方法 : 一、手动获取: 管理员后台——审批——找到对应的表单:如图: ProcessCode后面就是了! 二、接口获取:今天的重点&a…

Redis消息队列-基于Stream的消息队列-消费者组

7.5 Redis消息队列-基于Stream的消息队列-消费者组 消费者组(Consumer Group):将多个消费者划分到一个组中,监听同一个队列。具备下列特点: 创建消费者组: key:队列名称 groupName&#xff1a…

SimpleImputer缺失数据处理报错解决方案

作者Toby,来源公众号:Python风控建模,SimpleImputer缺失数据处理报错解决方案 今天有学员反馈缺失值代码报错,由于sklearn缺失值处理的包升级,下面把官网最新的缺失值处理代码奉上。 参考https://scikit-learn.org/st…

LeetCode-热题100:101. 对称二叉树

题目描述 给你一个二叉树的根节点 root , 检查它是否轴对称。 示例 1: 输入: root [1,2,2,3,4,4,3] 输出: true 示例 2: 输入: root [1,2,2,null,3,null,3] 输出: false 提示:…

数据库讲解---(数据更新、视图、数据控制)【MySQL版本】

目录 前言 一.数据更新 1.1插入数据 1.1.1插入单个元组 1.1.2将一个新学生记录(学号:091530,姓名:夏雨,性别:男,籍:海南,出生年份:1999,学院:计算机)插入到学生表中 1.1.3插入子查询结果 1.1.4有一个表“DEPT”(SDEPT CHAR(20),AVG_AGE SMALLINT)表示每个学院的学生的平…

蓝桥杯2024年第十五届省赛真题-R 格式(高精度乘法 + 加法)

本题链接:蓝桥杯2024年第十五届省赛真题-R 格式 - C语言网 题目:​​​​​​​ 样例: 输入 2 3.14 输出 13 思路: 根据题意,结合数据范围,这是一道模板的高精度乘以低精度问题。 题意是double 类型 d 与…

深度解析 Spark(进阶):架构、集群运行机理与核心组件详解

关联阅读博客文章:深度解析SPARK的基本概念 引言: Apache Spark作为一种快速、通用、可扩展的大数据处理引擎,在大数据领域中备受关注和应用。本文将深入探讨Spark的集群运行原理、核心组件、工作原理以及分布式计算模型,带领读者…

spring webflux 小结

一、WebFlux 简介 WebFlux 是 Spring Framework5.0 中引入的一种新的反应式Web框架。通过Reactor项目实现Reactive Streams规范,完全异步和非阻塞框架。本身不会加快程序执行速度,但在高并发情况下借助异步IO能够以少量而稳定的线程处理更高的吞吐&…

今日arXiv最热NLP大模型论文:一文读懂大模型的prompt技术

引言:探索高效提示方法的重要性 在人工智能领域,大语言模型(LLMs)已经成为了自然语言处理(NLP)任务的重要工具。随着模型规模的不断扩大,如何高效地利用这些模型,尤其是在资源有限的…

Redis中的订阅发布和事务(一)

订阅发布 PUBSUB NUMSUB PUBSUB NUMSUB [channel-1 channel-2… channel-n]子命令接受任意多个频道作为输入参数,并返回这些频道的订阅者数量。 这个子命令是通过pubsub_channels字典中找到频道对应的订阅者链表,然后返回订阅者链表的长度来实现的(订阅…

随机游走的艺术-图嵌入表示学习

图嵌入引入 机器学习算法: 厨师 样本集: 食材 只有好的食材才能做出好的饭菜 我们需要把数据变成计算机能够读懂的形式(将数据映射成为向量) 图嵌入概述 传统图机器学习 图表示学习 自动学习特征,将…
最新文章