当前位置: 首页 > news >正文

Go可以使用设计模式,但绝不是《设计模式》中的那样

文章精选推荐

1 JetBrains Ai assistant 编程工具让你的工作效率翻倍
2 Extra Icons:JetBrains IDE的图标增强神器
3 IDEA插件推荐-SequenceDiagram,自动生成时序图
4 BashSupport Pro 这个ides插件主要是用来干嘛的 ?
5 IDEA必装的插件:Spring Boot Helper的使用与功能特点
6 Ai assistant ,又是一个写代码神器
7 Cursor 设备ID修改器,你的Cursor又可以继续试用了

文章正文

Go语言是一门简洁、高效的编程语言,它支持面向对象编程的一些特性,比如接口、封装和组合,但并不直接提供类、继承等传统的面向对象概念。

因此,Go语言在使用设计模式时,有一些独特的方式和哲学,与传统面向对象语言(如 Java、C++)中的设计模式实现方式有所不同。

本文将深入探讨 Go 如何使用设计模式,并强调与《设计模式》中的传统实现的不同点,并通过代码示例进行说明。


1. Go 中的设计哲学

在 Go 中,有以下设计哲学与传统设计模式实现密切相关:

  • 组合优于继承:Go 提倡通过组合而不是继承来实现代码复用,避免了复杂的类层次结构。
  • 接口解耦:接口是 Go 语言中非常核心的特性,允许我们定义行为契约,而不依赖具体实现。
  • 简洁优先:Go 鼓励通过简单、直接的方式解决问题,而不是引入复杂的模式。

因此,在 Go 中,很多经典设计模式会简化,甚至在某些情况下,Go 原生特性(如接口、goroutine)已经能直接解决问题。


2. Go 中的常见设计模式解析

2.1 单例模式

传统实现
在面向对象语言中,单例模式通常需要通过私有构造函数、静态变量和双重检查锁定来实现线程安全的单例。

Go 实现
在 Go 中,可以通过 sync.Once 来实现线程安全的单例模式,代码更加简洁。

package mainimport ("fmt""sync"
)// 单例结构体
type Singleton struct{}var (instance *Singletononce     sync.Once
)// 获取单例实例
func GetInstance() *Singleton {once.Do(func() {instance = &Singleton{}})return instance
}func main() {s1 := GetInstance()s2 := GetInstance()fmt.Println(s1 == s2) // true
}

关键点

  • 使用 sync.Once 确保单例只初始化一次,避免手动实现锁和双重检查。

2.2 工厂模式

传统实现
在传统语言中,工厂模式通常通过一个基类(或接口)和具体子类实现。

Go 实现
由于 Go 语言没有类,工厂模式可以通过接口来抽象行为,通过函数直接创建实例。

package mainimport "fmt"// 动物接口
type Animal interface {Speak() string
}// 狗的实现
type Dog struct{}func (d Dog) Speak() string {return "Woof!"
}// 猫的实现
type Cat struct{}func (c Cat) Speak() string {return "Meow!"
}// 工厂函数
func NewAnimal(animalType string) Animal {switch animalType {case "dog":return Dog{}case "cat":return Cat{}default:return nil}
}func main() {dog := NewAnimal("dog")fmt.Println(dog.Speak()) // Woof!cat := NewAnimal("cat")fmt.Println(cat.Speak()) // Meow!
}

关键点

  • 使用简单的工厂函数代替复杂的类和构造函数。
  • 动物的行为通过接口抽象,而具体类型由工厂函数决定。

2.3 策略模式

传统实现
策略模式通常通过抽象基类和多个具体子类来实现不同策略。

Go 实现
在 Go 中,可以直接使用接口和函数作为策略实现。

package mainimport "fmt"// 策略接口
type PaymentStrategy interface {Pay(amount float64)
}// 信用卡支付
type CreditCard struct{}func (cc CreditCard) Pay(amount float64) {fmt.Printf("Paid %.2f using Credit Card.\n", amount)
}// PayPal 支付
type PayPal struct{}func (pp PayPal) Pay(amount float64) {fmt.Printf("Paid %.2f using PayPal.\n", amount)
}// 使用策略模式
func ProcessPayment(strategy PaymentStrategy, amount float64) {strategy.Pay(amount)
}func main() {cc := CreditCard{}pp := PayPal{}ProcessPayment(cc, 100.0) // Paid 100.00 using Credit Card.ProcessPayment(pp, 200.0) // Paid 200.00 using PayPal.
}

关键点

  • 使用接口定义策略行为,通过具体实现来提供不同的策略。
  • 策略选择可以在运行时动态更改。

2.4 观察者模式

传统实现
观察者模式通过一个主题(Subject)和多个观察者(Observer)实现,主题通知观察者更新。

Go 实现
在 Go 中,可以通过通道(channel)实现观察者模式,利用 goroutine 提供异步通知。

package mainimport "fmt"// 观察者
type Observer interface {Update(data string)
}// 具体观察者
type ConcreteObserver struct {id string
}func (co *ConcreteObserver) Update(data string) {fmt.Printf("Observer %s received: %s\n", co.id, data)
}// 主题
type Subject struct {observers []Observer
}func (s *Subject) Register(observer Observer) {s.observers = append(s.observers, observer)
}func (s *Subject) Notify(data string) {for _, observer := range s.observers {observer.Update(data)}
}func main() {subject := &Subject{}observer1 := &ConcreteObserver{id: "1"}observer2 := &ConcreteObserver{id: "2"}subject.Register(observer1)subject.Register(observer2)subject.Notify("Hello Observers!")
}

关键点

  • 使用接口定义观察者行为。
  • Notify 方法遍历所有观察者并调用其 Update 方法。

3. Go 的特色替代方案

在某些情况下,Go 的原生特性(如 goroutine、channel)可以替代传统设计模式。

3.1 使用 goroutine 替代命令模式

传统的命令模式通过封装请求和执行逻辑来解耦调用者和执行者。在 Go 中,可以直接通过 goroutine 和通道实现异步执行。

package mainimport "fmt"func main() {commandQueue := make(chan func(), 10)// 启动一个工作 goroutinego func() {for command := range commandQueue {command() // 执行命令}}()// 添加命令到队列commandQueue <- func() { fmt.Println("Command 1 executed") }commandQueue <- func() { fmt.Println("Command 2 executed") }close(commandQueue)
}

4. 总结

  • Go 的简单哲学:Go 通过接口、组合、goroutine 等特性,简化了很多传统设计模式的实现。
  • 减少复杂性:在 Go 中,我们可以直接使用函数、接口等轻量级工具来实现模式,而不是强依赖复杂的类和继承关系。
  • 场景驱动:Go 语言中的设计模式不是为了满足某种“模式理论”,而是为了适应具体的业务需求,强调代码的简洁和可读性。

Go 语言在使用设计模式时,更注重实践而不是形式。这种哲学让开发者能够专注于解决问题,而不是被复杂的模式实现所束缚。

相关文章:

Go可以使用设计模式,但绝不是《设计模式》中的那样

文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons&#xff1a;JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram&#xff0c;自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 &#xff1f; 5 IDEA必装的插件&…...

【C语言】_使用冒泡排序模拟实现qsort函数

目录 1. 排序函数的参数 2. 排序函数函数体 2.1 比较元素的表示 2.2 交换函数Swap的实现 2.3 排序函数bubble_sort的实现 3. 测试整型数据排序 3.1 整型数据比较函数cmp_int的实现 3.2 整型数据排序后输出函数print_int的实现 3.3 整型数据测试函数test_int的实现 3…...

openCvSharp 计算机视觉图片找茬

一、安装包 <PackageReference Include"OpenCvSharp4" Version"4.10.0.20241108" /> <PackageReference Include"OpenCvSharp4.runtime.win" Version"4.10.0.20241108" /> 二、准备两张图片 三、编写代码 using OpenCv…...

从零开始开发纯血鸿蒙应用之处理外部文件

从零开始开发纯血鸿蒙应用 一、外部文件二、外部文件的访问形式1、主动访问2、被动访问 三、代码实现1、DocumentViewPicker2、Ability Skills3、onNewWant 函数4、冷启动时处理外部文件 一、外部文件 对于移动端app来说&#xff0c;什么是外部文件呢&#xff1f;是那些存储在…...

Spring中三级缓存详细讲解

1、Spring三级缓存是什么&#xff0c;过程是怎么样的&#xff1f; Spring 中的三级缓存主要用于单例 Bean 的生命周期管理&#xff0c;特别是在循环依赖时&#xff0c;它通过不同阶段暴露 Bean 实例来确保依赖注入的顺利完成。缓存的内容如下&#xff1a; 一级缓存 (singleton…...

论文阅读:《Whole-animal connectomes of both Caenorhabditis elegans sexes》

一 论文整体概述 论文下载链接&#xff1a;《Whole-animal connectomes of both Caenorhabditis elegans sexes》 补充信息和额外数据&#xff1a;https://www.nature.com/articles/s41586-019-1352-7 1. 作者期刊背景 该论文由Scott W. Emmons&#xff0c;David H. Hall等…...

嵌入式开发之STM32学习笔记day03

STM32之ADC&#xff08;模拟数字转换器&#xff09; 1 ADC简述2 ADC转换时间3 ADC转化结果存放机制4 ADC转化结果存放机制5 ADC电压转换 1 ADC简述 ADC&#xff08;Analog-Digital Converter&#xff09;模拟—数字转换器&#xff1b;ADC可以将引脚上连续变化的模拟电压转换为…...

windows10 安装 Golang 版本控制工具g与使用

下载包&#xff1a;https://github.com/voidint/g/releases 解压&#xff0c; 并添加到环境变量 g 常用命令 查询当前可供安装的stable状态及所有的 go 版本 # stable 版本 g ls-remote stable# 所有版本 g ls-remote安装目标 go 版本1.23.4g install 1.23.4切换到已安装的…...

SpringBoot 使用 Cache 集成 Redis做缓存保姆教程

1. 项目背景 Spring Cache是Spring框架提供的一个缓存抽象层&#xff0c;它简化了缓存的使用和管理。Spring Cache默认使用服务器内存&#xff0c;并无法控制缓存时长&#xff0c;查找缓存中的数据比较麻烦。 因此Spring Cache支持将缓存数据集成到各种缓存中间件中。本文已常…...

R数据分析:多分类问题预测模型的ROC做法及解释

有同学做了个多分类的预测模型,结局有三个类别,做的模型包括多分类逻辑回归、随机森林和决策树,多分类逻辑回归是用ROC曲线并报告AUC作为模型评估的,后面两种模型报告了混淆矩阵,审稿人就提出要统一模型评估指标。那么肯定是统一成ROC了,刚好借这个机会给大家讲讲ROC在多…...

数据结构与算法之二叉树: LeetCode 654. 最大二叉树 (Ts版)

最大二叉树 https://leetcode.cn/problems/maximum-binary-tree/ 描述 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值递归地在最大值 左边 的 子数组前缀上 构建左子树递归地在最大值…...

Linux 容器漏洞

定义&#xff1a;Linux 容器漏洞是指在容器技术&#xff08;如 Docker、LXC 等&#xff09;运行环境中存在的安全弱点。这些漏洞可能存在于容器镜像本身、容器运行时&#xff08;如 runc&#xff09;、容器编排工具&#xff08;如 Kubernetes&#xff09;或者容器与主机之间的交…...

file与io流(1)

-1- java.io.File类的使用 &#xff08;1&#xff09; 概述 File类及本章下的各种流&#xff0c;都定义在java.io包下。一个File对象代表硬盘或网络中可能存在的一个文件或者文件目录&#xff08;俗称文件夹&#xff09;&#xff0c;与平台无关。&#xff08;体会万事万物皆…...

忘记了PDF文件的密码,怎么办?

PDF文件可以加密&#xff0c;大家都不陌生&#xff0c;并且大家应该也都知道PDF文件有两种密码&#xff0c;一个打开密码、一个限制编辑密码&#xff0c;因为PDF文件设置了密码&#xff0c;那么打开、编辑PDF文件就会受到限制。忘记了PDF密码该如何解密&#xff1f; PDF和offi…...

Linux权限管理(用户和权限之间的关系)

Linux系列 文章目录 Linux系列一、Linux下用户类型二、普通权限的基本概念2.1、Linux中权限的类别2.2、Linux中权限对应的三种身份2.3、文件权限的标识 三、文件权限设置四、修改文件属主和属组4.1、chown修改文件的属主4.2、修改所属组 五、文件掩码六、目录权限 一、Linux下用…...

Python Selenium库入门使用,图文详细。附网页爬虫、web自动化操作等实战操作。

文章目录 前言1 创建conda环境安装Selenium库2 浏览器驱动下载&#xff08;以Chrome和Edge为例&#xff09;3 基础使用&#xff08;以Chrome为例演示&#xff09;3.1 与浏览器相关的操作3.1.1 打开/关闭浏览器3.1.2 访问指定域名的网页3.1.3 控制浏览器的窗口大小3.1.4 前进/后…...

【Uniapp-Vue3】使用defineExpose暴露子组件的属性及方法

如果我们想要让父组件访问到子组件中的变量和方法&#xff0c;就需要使用defineExpose暴露&#xff1a; defineExpose({ 变量 }) 子组件配置 父组件配置 父组件要通过onMounted获取到子组件的DOM 传递多个属性和方法 子组件 父组件...

【多模态LLM】英伟达NVLM多模态大模型训练细节和数据集

前期笔者介绍了OCR-free的多模态大模型&#xff0c;可以参考&#xff1a;【多模态&文档智能】OCR-free感知多模态大模型技术链路及训练数据细节&#xff0c;其更偏向于训练模型对于密集文本的感知能力。本文看一看英伟达出品的多模态大模型NVLM-1.0系列&#xff0c;虽然暂未…...

HTTP详解——HTTP基础

HTTP 基本概念 HTTP 是超文本传输协议 (HyperText Transfer Protocol) 超文本传输协议(HyperText Transfer Protocol) HTTP 是一个在计算机世界里专门在 两点 之间 传输 文字、图片、音视频等 超文本 数据的 约定和规范 1. 协议 约定和规范 2. 传输 两点之间传输&#xf…...

MySQL教程之:输入查询

如上一节所述&#xff0c;确保您已连接到服务器。这样做本身不会选择任何要使用的数据库&#xff0c;但没关系。在这一点上&#xff0c;了解一下如何发出查询比直接创建表、加载数据和从中检索数据更重要。本节介绍输入查询的基本原则&#xff0c;使用几个查询&#xff0c;您可…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

省略号和可变参数模板

本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...