Go设计模式 - 创建型模式
3. 创建型模式 模式名称 模式名称 作用 创建型模式 Creational Pattern(6) 单例模式★★★★☆ 是保证一个类仅有一个实例,并提供一个访问它的全局访问点。 简单工厂模式★★★☆☆ 通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。 工厂方法模式★★★★★ 定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。 抽象工厂模式★★★★★ 提供一个创建一系列相关或者相互依赖的接口,而无需指定它们具体的类。 原型模式★★★☆☆ 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 建造者模式★★☆☆☆ 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 目前标准的创建型设计模式共有6种(注:设计模式种类并非仅仅局限于此,设计模式实则是一种编程思想,开发者可以根据自身经验来总结出很多种设计模式思想,这6中创建型设计模式为早期官方认可的标准模式) 本章节主要介绍常用的“单例模式”、“简单工程模式”、“工厂方法模式”、“抽象工厂模式”等。“原型模式”、“建造...
Go设计模式 - 面向对象设计原则
2. 面向对象设计原则 对于面向对象软件系统的设计而言,在支持可维护性的同时,提高系统的可复用性是一个至关重要的问题,**如何同时提高一个软件系统的可维护性和可复用性是面向对象设计需要解决的核心问题之一。**在面向对象设计中,可维护性的复用是以设计原则为基础的。每一个原则都蕴含一些面向对象设计的思想,可以从不同的角度提升一个软件结构的设计水平。 **面向对象设计原则为支持可维护性复用而诞生,这些原则蕴含在很多设计模式中,它们是从许多设计方案中总结出的指导性原则。**面向对象设计原则也是我们用于评价一个设计模式的使用效果的重要指标之一。 原则的目的: 高内聚,低耦合 2.1 面向对象设计原则表 名称 定义 单一职责原则(Single Responsibility Principle, SRP)★★★★☆ 类的职责单一,对外只提供一种功能,而引起类变化的原因都应该只有一个。 开闭原则(Open-Closed Principle, OCP)★★★★★ 类的改动是通过增加代码进行的,而不是修改源代码。 里氏代换原则(Liskov Substitution Princ...
Go设计模式 - 概述
1. Go设计模式概述 如果把修习软件开发当做武功修炼的话,那么可以分为招式和内功。 招式: ●Java、C#、C++、Golang、Rust等编程语言; ● Eclipse、Visual Studio、Goland、Vim等开发工具; ● Struts、Hibernate、JBPM、Gin、Istio、gRPC等框架技术; 内功: ●数据结构 ●算法 ●设计模式 ●架构设计 ●软件工程 注意:招式可以很快学会,但是内功的修炼需要更长的时间 1.1 设计模式从何而来 上图是“模式之父”,Christopher Alexander(克里斯托弗.亚历山大)———哈佛大学建筑学博士、美国加州大学伯克利分校建筑学教授、加州大学伯克利分校环境结构研究所所长、美国艺术和科学院院士。 克里斯托弗.亚历山大在作品《建筑的永恒之道》中对“模式”的描述是: “每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心,通过这种方式,我们可以无数次地重用那些已有的成功的解决方案,无须再重复相同的工作。” 他给出了设计模式的定义。 我们也可以用下面这句话来理解“设计模式”的...
常见坑1~10
1.常见坑1~10 01.nil slice & empty slice 1.1 nil切片与空切片底层 nil切片:var nilSlice []string nil slice 的长度len和容量cap都是0 nil slice == nil nil slice的pointer 是nil, 空切片:emptySlice0 := make([]int, 0) empty slice的长度是0, 容量是由指向底层数组决定 empty slice != nil empty slice的pointer是底层数组的地址 nil切片和空切片最大的区别在于指向的数组引用地址是不一样的。 nil空切片引用数组指针地址为0(无指向任何实际地址) 空切片的引用数组指针地址是有的,且固定为一个值,所有的空切片指向的数组引用地址都是一样的 1.2 创建nil slice和empty slice 1234567891011121314151617181920package mainimport "fmt"func main() { var ...
Golang常犯错误
1.Golang常犯错误 01.01~10 01.nil的slice和map 允许对值为 nil 的 slice 添加元素,但对值为 nil 的 map 添加元素,则会造成运行时 panic。 123456789101112// map 错误示例func main() { var m map[string]int m["one"] = 1 // error: panic: assignment to entry in nil map // m := make(map[string]int)// map 的正确声明,分配了实际的内存} // slice 正确示例func main() { var s []int s = append(s, 1)} 02.判断map中key是否存在 当访问 map 中不存在的 key 时,Go 则会返回元素对应数据类型的零值,比如 nil、’’ 、false 和 0 取值操作总有值返回,故不能通过取出来的值,来判断 key 是不是在 map 中...
mutex锁原理
1.mutex锁原理 01.Mutex 1.1 mutex结构体 源码包src/sync/mutex.go:Mutex定义了互斥锁的数据结构 1234type Mutex struct { state int32 // 表示互斥锁的状态,比如是否被锁定等 sema uint32 // 表示信号量,协程阻塞等待该信号量,解锁的协程释放信号量从而唤醒等待信号量的协程} 我们看到Mutex.state是32位的整型变量,内部实现时把该变量分成四份,用于记录Mutex的四种状态。 下图展示Mutex的内存布局 Locked: 表示该Mutex是否已被锁定,0:没有锁定 1:已被锁定。 Woken: 表示是否有协程已被唤醒,0:没有协程唤醒 1:已有协程唤醒,正在加锁过程中。 Starving:表示该Mutex是否处理饥饿状态, 0:没有饥饿 1:饥饿状态,说明有协程阻塞了超过1ms。 Waiter: 表示阻塞等待锁的协程个数,协程解锁时根据此值来判断是否需要释放信号量。 1.2 简单加锁 假定当前只有一个协程在加锁,没有其他协程干扰,那...
sync.Map
1.sync.map 01.sync.Map介绍 1.1 sync.Map介绍 简单说:空间换时间+读写分离+原子操作(快路径) sync.Map 的主要思想就是读写分离,空间换时间 。 sync.Map底层使用了两个原生map,一个叫read,仅用于读; 一个叫dirty,用于在特定情况下存储最新写入的key-value数据 1.2 sync.Map特点 1、空间换时间:通过冗余的两个Map数据结构(read、dirty),实现加锁对性能的影响。 2、使用只读数据(read),避免读写冲突。 3、动态调整,miss次数多了之后,将dirty数据迁移到read中。 4、double-checking。 5、迟删除。 删除一个键值只是打标记,只有在迁移dirty数据的时候才清理删除的数据。 6、优先从read读取、更新、删除,因为对read的读取不需要锁。 1.3 sync.Map结构体 12345678910111213type Map struct { // 当涉及到脏数据(dirty)操作时候,需要使用这个锁 mu Mutex ...
sync.Pool
1.sync.Pool 01.sync.Pool介绍 1.1 是什么 sync.Pool 是 sync 包下的一个组件,可以作为保存临时取还对象的一个“池子”。 个人觉得它的名字有一定的误导性,因为 Pool 里装的对象可以被无通知地被回收,可能 sync.Cache 是一个更合适的名字。 Pool 结构体的定义为: Pool 中有两个定义的公共方法,分别是 Put - 向池中添加元素; Get 从池中获取元素,如果没有,则调用 New 生成元素,如果 New 未设置,则返回 nil。 12345678910type Pool struct { noCopy noCopy local unsafe.Pointer // 本地P缓存池指针 localSize uintptr // 本地P缓存池大小 // 当池中没有可能对象时 // 会调用 New 函数构造构造一个对象 New func() interface{}} 1.2 有什么用 对于很多需要重复分配、回收内存的地方,sync.Po...
协程调度GMP模型
1.协程调度GMP模型 01.线程调度 1.1 早期单线程操作系统 一切的软件都是跑在操作系统上,真正用来干活(计算)的是CPU。 早期的操作系统每个程序就是一个进程,知道一个程序运行完,才能进行下一个进程,就是“单进程时代” 一切的程序只能串行发生。 1.2 多进程/线程时代 在多进程/多线程的操作系统中,就解决了阻塞的问题,因为一个进程阻塞cpu可以立刻切换到其他进程中去执行 而且调度cpu的算法可以保证在运行的进程都可以被分配到cpu的运行时间片 这样从宏观来看,似乎多个进程是在同时被运行。 但新的问题就又出现了,进程拥有太多的资源,进程的创建、切换、销毁,都会占用很长的时间 CPU虽然利用起来了,但如果进程过多,CPU有很大的一部分都被用来进行进程调度了 大量的进程/线程出现了新的问题 高内存占用 调度的高消耗CPU 进程虚拟内存会占用4GB[32位操作系统], 而线程也要大约4MB 1.3 Go协程goroutine Go中,协程被称为goroutine,它非常轻量,一个goroutine只占几KB,并且这几KB就足够goroutine运行完 这就能...
chan读写问题
1.chan读写问题 01.对关闭chan读写 golang面试题:对已经关闭的的chan进行读写,会怎么样?为什么? 读已经关闭的 chan 能一直读到东西,但是读到的内容根据通道内关闭前是否有元素而不同。 1)读取有元素,且关闭的chan 会正确读到 chan 内的值,且返回的第二个 bool 值(是否读成功)为 true。 2)读取无元素,且关闭的chan chan 内无值,接下来所有接收的值都会非阻塞直接成功 返回 channel 元素的零值,但是第二个 bool 值一直为 false。 3)写已经关闭的 chan 会 panic 02.未初始化的的chan读写 对未初始化的的chan进行读写,会怎么样?为什么? 2.1 对于写的情况 未初始化的 chan 此时是等于 nil,当它不能阻塞的情况下,直接返回 false,表示写 chan 失败 当 chan 能阻塞的情况下,则直接阻塞 gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2) 然后调用 throw(s string...










