自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(317)
  • 收藏
  • 关注

原创 grpool协程池—控制goroutine数量利器

目的grpool的目的就是控制执行任务的goroutine数量,避免创建过多的goroutine导致内存占用飙升简单使用func UseWorkerPool() { pool := grpool.NewPool(100, 50) defer pool.Release() for i := 0; i < 10; i++ { count := i pool.JobQueue <- func() { fmt.Prin

2021-10-09 15:36:07 688 1

原创 编程规范实战—ID生成器

需求背景假设你正在参与一个后端业务系统的开发,为了方便在请求出错时排查问题,我们在编写代码的时候会在关键路径上打印日志。某个请求出错之后,我们希望能搜索出这个请求对应的所有日志,以此来查找问题的原因。而实际情况是,在日志文件中,不同请求的日志会交织在一起。如果没有东西来标识哪些日志属于同一个请求,我们就无法关联同一个请求的所有日志。我们可以给每个请求分配一个唯一 ID,并且保存在请求的上下文(Context)中。每次打印日志的时候,我们从请求上下文中取出请求 ID,跟日志一块输出。这样,同一个请求的所有

2021-09-18 10:55:22 1008

原创 编码规范—编程技巧

提炼类或者函数将函数里复杂的大块逻辑提炼成单独的类或者函数如果逻辑不复杂,提炼成函数后也就两三行,那就没必要提炼出来,阅读的时候还得跳一下,反倒增加阅读成本避免函数参数过多函数3、4个参数还是可以接受的,大于等于5个就有点多了,会影响代码的可读性,使用起来也不方便如果函数参数比较多,那么有两种处理方法1)考虑函数的职责是否单一,能不能拆成多个函数来减少参数个数示例如下func GetUser(username string, telephone string, email string) {

2021-09-16 11:09:50 226

原创 编码规范—代码风格

类、函数多大才合适函数代码行数的最大限制:不要超过显示器的垂直高度类代码行数的最大限制:没有一个确切的值,只有一个间接的判断标准。当一个类的代码读起来让你感觉头大,实现某个功能时不知道用哪个函数,想用哪个函数半天找不到,只用到一个小功能却要引入整个大类,那就说明类的代码行数过多了一行代码多长合适一行代码不要超过显示器的水平宽度善用空行分割单元块对于比较长的函数,如果逻辑上可以分为几个独立的代码块,在不方便将这些代码块抽成小函数的情况下,可以用空行分割各个代码块以及使用总结性注释大括号要不要另起

2021-09-16 10:54:47 152

原创 编码规范—命名、注释

命名命名的目标就是准确达意长度对于一些默认的、大家都比较熟知的词可以使用缩写。比如second使用sec、document使用doc、number使用num。对于作用域比较小的变量,使用相对短的命名,比如一些函数内的临时变量;对于类名这类作用域比较大的推荐使用长的命名可以利用类上下文缩短成员变量的命名,利用函数上下文缩短函数参数的命名可读、可搜索可读:不要使用一些比较生僻、难读的词可搜索:命名符合项目规范。大家都用"SelectXXX",你就不要用"QueryXXX";大家都用"Insert

2021-09-16 10:51:09 278

原创 设计原则实战—性能计数框架

项目背景我们希望设计开发一个小的框架,能够获取接口调用的各种统计信息,比如,响应时间的最大值(max)、最小值(min)、平均值(avg)、百分位值(percentile)、接口调用次数(count)、频率(tps) 等,并且支持将统计结果以各种显示格式(比如:JSON 格式、网页格式、自定义显示格式等)输出到各种终端(Console 命令行、HTTP 网页、Email、日志文件、自定义输出终端等)需求分析功能性需求1)获取接口调用的统计信息。包括:响应时间的统计信息、调用次数的统计信息2)统计信

2021-09-13 11:45:28 121

原创 设计原则实战—电商平台积分系统

需求背景设计一个简单的电商平台积分系统需求分析借鉴竞品先去借鉴一下其他电商平台的积分系统是怎么设计的,可以去使用一下淘宝的积分系统或者百度搜一下"淘宝积分兑换规则",就能摸清积分系统大致的功能。积分系统的功能简单分成两类:积分的赚取渠道和兑换规则,赚取渠道比如有下单、签到、答题等,兑换规则比如有签到送10积分,答题全对送10积分,下单后返送某个下单金额比例的积分等;积分的消费渠道和兑换规则,消费渠道比如有下单抵钱、换优惠券等,兑换规则比如下单时按照某个比例抵扣用户拥有的积分、10积分换一张特定的优

2021-09-13 11:35:55 1403

原创 一文搞懂迪米特法则

定义没有直接依赖关系的类不要依赖,即便类有依赖关系也只应该依赖必要的接口高内聚、低耦合什么是"高内聚、低耦合"高内聚、低耦合是一种思想,能够有效提高代码的可复用性、可读性、可维护性,减小代码修改影响的范围"高内聚"用来指导类的设计,"低耦合"用来指导类之间依赖关系的设计。高内聚有助于低耦合。什么是高内聚高内聚就是将相近的功能放到一个类里面,不相近的功能放到不同的类里面,相近的功能往往会被同时修改,放到一个类里面,修改的时候就更集中什么是低耦合没有直接依赖关系的类不要依赖,即便有依赖关系也只

2021-09-10 11:14:17 160

原创 一文搞懂DRY原则

定义不要重复自己注意:重复的代码不一定违反DRY原则,不重复的代码也有可能违反DRY原则三种典型场景代码逻辑重复功能语义重复代码重复执行代码逻辑重复比如UserService有两个方法,一个IsValidUserName校验用户名,一个IsValidPassword校验密码type UserService2 struct{}func (s *UserService2) IsValidUserName(username string) bool { if username == ""

2021-09-10 10:58:50 674

原创 一文搞懂Kiss、Yagni原则

Kiss原则定义尽量保持简单目的Kiss原则是保持代码可读性、可维护性的有效手段,代码简单那么就容易理解,bug就越难隐藏,即便出现bug修复起来也比较简单代码行数越少就越"简单"吗?不是,不仅要看代码行数,还需要考虑代码的可理解性、逻辑复杂度、可读性主要还是看代码的可理解性,代码越容易理解我们才认为代码越简单,如果可理解性差不多那么选择代码行数少的写法代码逻辑复杂就违背Kiss原则吗?不是,有些算法或者业务逻辑本身就非常复杂,那么对应的代码实现自然也就复杂,这是无法避免的如何写出满足K

2021-09-08 11:02:32 252

原创 一文搞懂依赖反转原则

定义依赖反转包括两部分:控制反转、依赖注入控制反转先看下面这段代码type UserServiceTest struct{}func (t *UserServiceTest) DoTest() bool { println("test logic") return true}func ApplicationMain() { u := new(UserServiceTest) if u.DoTest() { println("test success") } else {

2021-09-08 10:56:46 535

原创 一文搞懂接口隔离原则

定义客户端不应该被强迫依赖它不需要的接口,这里的客户端指接口的调用者接口可以理解为下面三种东西:一组API接口集合单个API接口或者函数OOP中的接口概念一组API接口集合一组API接口集合指的就是微服务或者类库比如开发一个用户系统微服务,提供了一组和用户相关的接口给外部系统使用type UserService interface { Register(username string, password string) Login(username string, password s

2021-09-07 11:04:19 456

原创 一文搞懂里式替换原则

原理子类对象能够替换父类对象,并且保证原来程序的逻辑性不变以及正确性不被破坏什么叫逻辑性不变?举个例子,原来父类处理的时候没有任何校验不会抛出error,但是子类新增了一个校验可能会抛出error,那么子类替换父类后程序的逻辑性就会被改变,就违反了LSP原则多态和里式替换原则的区别多态:是一种代码实现思路里式替换原则:是一种设计原则,用来指导子类的设计,子类替换父类的时候不能改变原来程序的逻辑、不能破坏原来程序的正确性怎么做到里式替换原则子类的设计要满足父类的行为约定,这些约定包括:函数声明

2021-09-07 10:53:46 116

原创 一文搞懂开闭原则

定义对扩展开放,对修改关闭详细说就是新增一个业务功能的时候,在已有代码的基础上去扩展(新增模块、类、方法),不要去修改已有代码(修改模块、类、方法)什么是合格的修改只要它没有破坏原有的代码的正常运行,没有破坏原有的单元测试,我们就可以说,这是一个合格的代码改动。添加一个新功能,不可能任何模块、类、方法的代码都不“修改”,这个是做不到的。哪怕在已有代码基础上扩展一个新的类,类需要创建、组装、并且做一些初始化操作,才能构建成可运行的的程序,这部分代码是无法避免的。我们要做的是尽量让修改操作更集中、更

2021-09-06 11:06:14 121

原创 一文搞懂单一职责原则

定义一个类或者模块只负责完成一个职责(功能)不要设计大而全的类,而要设计粒度小、功能单一的类。简单说就是如果一个类包含两个或者两个以上不相干的业务功能,我们就说这个类不符合单一职责原则,应该将这个类拆成多个功能更加单一、粒度更细的类如何判断类的职责足够单一举个例子:在一个社交产品中,使用UserInfo表示用户信息,UserInfo类结构如下type UserInfo struct { UserID uint64 UserName string Ema

2021-09-03 11:00:07 219

原创 一文搞懂中介模式

原理定义一个中介对象,将一组对象之间的交互转成和中介对象的交互,避免对象之间的复杂交互最佳实践需求背景假设我们有一个比较复杂的对话框,对话框中有很多控件,比如按钮、文本框、下拉框等。当我们对某个控件进行操作的时候,其他控件会做出相应的反应,比如,我们在下拉框中选择“注册”,注册相关的控件就会显示在对话框中。如果我们在下拉框中选择“登陆”,登陆相关的控件就会显示在对话框中。按照通常我们习惯的 UI 界面的开发方式,我们将刚刚的需求用代码实现出来,就是下面这个样子。在这种实现方式中,控件和控件之间互

2021-09-02 10:51:45 214

原创 一文搞懂解释器模式

原理为新的"语言"定义语法规则,定义一个解释器用来解释语法实现核心将不同操作符的操作拆分到单独的操作符表达式类里面,避免大而全的解析类最佳实践1需求背景假设我们定义了一个新的加减乘除计算“语言”,语法规则如下:运算符只包含加、减、乘、除,并且没有优先级的概念;表达式(也就是前面提到的“句子”)中,先书写数字,后书写运算符,空格隔开;按照先后顺序,取出两个数字和一个运算符计算结果,结果重新放入数字的最头部位置,循环上述过程,直到只剩下一个数字,这个数字就是表达式最终的计算结果。比如“ 8

2021-08-31 11:26:15 86

原创 一文搞懂命令模式

原理将命令封装成对象,从而对命令进行一些附加控制(延迟、异步、排队执行,撤销重做,存储,记录日志等等)目的控制命令的执行最佳实践需求背景假设我们正在开发一个类似《天天酷跑》或者《QQ 卡丁车》这样的手游。这种游戏本身的复杂度集中在客户端。后端基本上只负责数据(比如积分、生命值、装备)的更新和查询,所以,后端逻辑相对于客户端来说,要简单很多。一般来说,游戏客户端和服务器之间的数据交互是比较频繁的,所以,为了节省网络连接建立的开销,客户端和服务器之间一般采用长连接的方式来通信。通信的格式有多种,比

2021-08-30 11:05:43 119

原创 一文搞懂备忘录模式

原理在不违反封装性的前提下,在对象外保存对象状态,用于后续将对象恢复为以前的状态应用场景用于恢复、撤销、防丢失最佳实践需求背景假设有这样一道面试题,希望你编写一个小程序,可以接收命令行的输入。用户输入文本时,程序将其追加存储在内存文本中用户输入“:list”,程序在命令行中输出内存文本的内容用户输入“:undo”,程序会撤销上一次输入的文本,也就是从内存文本中将上次输入的文本删除掉代码实现—版本1type InputText struct { text string}func

2021-08-26 10:58:00 101

原创 一文搞懂访问者模式

带你"发明"访问者模式假设我们从网站上爬取了很多资源文件,它们的格式有三种:PDF、PPT、Word。我们现在要开发一个工具来处理这批资源文件。这个工具的其中一个功能是,把这些资源文件中的文本内容抽取出来放到 txt 文件中。如果让你来实现,你会怎么来做呢?第一版实现type ResourceFile struct { filePath string}type IResourceFile interface { Extract2Txt()}type PDFFile struct {

2021-08-25 11:09:58 108

原创 一文搞懂迭代器模式

原理迭代器模式将集合的遍历和集合本身分开,将遍历拆分到迭代器类,让每个类的职责更统一目的用来遍历集合对象最佳实践需求背景实现一个数组集合类,针对该集合类实现一个对应的迭代器,用来遍历集合中的元素代码实现定义数组集合类ArrayListtype ArrayList struct { data []interface{}}func (l *ArrayList) Add(item interface{}) { l.data = append(l.data, item)}func

2021-08-20 11:11:33 102

原创 一文搞懂状态模式

原理状态机有三个组成部分:状态、事件、动作。遇到不同的事件会触发状态的转移和动作的执行,不过动作不是必须的,可能只有状态的转移,没有动作的执行状态模式的目的就是实现状态机案例带入比如"超级马里奥",在游戏中,马里奥可以变身为多种形态,比如小马里奥(Small Mario)、超级马里奥(Super Mario)、火焰马里奥(Fire Mario)、斗篷马里奥(Cape Mario)等等。在不同的游戏情节下(遇到不同的事件),各个形态会互相转化,并相应的增减积分。比如,初始形态是小马里奥,吃了蘑菇之后就

2021-08-18 11:39:50 309

原创 一文搞懂责任链模式

原理将多个处理器连成一个链条,处理器A处理请求成功后,将请求交给处理器B,处理器B处理成功后再交给处理器C,以此类推,链条上每个处理器各自承担自己的职责。如果处理器处理失败,那么不再继续往后传递处理。实现方式方式1定义IDoHandler接口,业务处理类直接实现该接口实现业务方法,定义一个Handler类,该类持有IDoHandler、Successor,IDoHandler代表具体业务实现,Successor代表下一个Handler;HandlerChain用来表示处理器链条,从数据结构上来说是一

2021-08-17 11:08:39 92

原创 一文搞懂享元模式

原理复用对象,节省内存,前提是对象必须是不可变对象所谓不可变对象就是指没有提供任何属性的set方法,对象创建完后就不能修改任何属性,目的是为了避免某个使用方修改了共享对象的属性对其他使用方造成影响实现方式将对象设计成享元(共享的单元),在内存中只保存一份,供多处引用,这样可以减少内存中对象的数据量达到节省内存的目的不仅相同的对象可以设计成享元,也可以将对象中相同的部分抽取出来,设计成享元,让大量相似对象引用这些享元最佳实践需求1开发一个象棋游戏,一个游戏厅中有上万个房间,每个房间有一个棋局。

2021-08-13 11:18:42 140

原创 一文搞懂组合模式

原理将数据组织成树状结构,统一单个对象和组合对象的处理逻辑目的主要用来处理树形结构的数据,如果数据满足树形结构那么使用组合模式,会使代码非常简洁最佳实践需求1设计一个类来表示文件系统中的目录,能方便地实现下面这些功能:动态地添加、删除某个目录下的子目录或文件;统计指定目录下的文件个数;统计指定目录下的文件总大小未使用组合模式type FileSystemNode struct { path string isFile bool subNodes []*FileSy

2021-08-12 11:16:51 122

原创 一文搞懂门面模式

原理定义一组高层接口,让系统更易用应用场景解决易用性接口通用性强那么功能就比较单一,实现一个大的功能可能需要使用到多个接口比较麻烦可以通过门面模式暴露一组高层接口,将单一的底层接口封装起来,提高易用性解决性能问题将原来多次接口调用变为一次接口调用,减少网络通信次数解决分布式事务问题比如用户注册功能需要调用用户注册接口、钱包创建接口来完成,如果要保证这两个接口的原子性需要使用分布式事务,但是可以通过提供一个高层接口将用户创建、钱包创建包在一个本地事务中来保证两个操作的原子性,这样

2021-08-12 11:06:18 57

原创 一文搞懂适配器模式

原理将不兼容的接口转换成兼容的接口,让原本因为接口不兼容无法一起工作的类可以一起工作实现方式适配器两种实现方式:类适配器、对象适配器。类适配器通过继承实现,对象适配器通过组合实现。代码实现ITarget是要兼容的目标接口,Adaptee是不兼容的类,通过适配器模式将Adaptee这个类转换成兼容的ITarget接口类适配器type ITarget interface { f1() f2() fc()}type Adaptee struct{}func (a *Adaptee)

2021-08-10 11:25:34 107

原创 一文搞懂装饰器模式

原理在不改变原始类代码的情况下,增强原始类的功能。注意这里的功能并不是非业务功能,而是对业务功能的增强比如FileInputStream、BufferFileInputStream,FileInputStream负责文件读取、BufferFileInputStream就是对文件读取功能的增强新加了带缓存的文件读取特殊点原始类、装饰类实现相同接口,可以做到嵌套装饰装饰类是对原始类纯功能的增量代码实现代码骨架type ICommon interface { F()}type Origin

2021-08-09 10:59:42 85

原创 一文搞懂桥接模式

原理将抽象和实现解耦,让它们可以独立变化这里的抽象不是指"接口"或者"抽象类",而是和具体实现无关的一套类库;这里的实现不是指"接口的实现类",而是和具体实现有关的一套类库这里的说法稍微有些抽象,看下面的需求实现更好理解需求实现需求背景根据不同的报警规则,进行不同类型的报警。报警包含多种通知渠道:邮件、短信、微信、电话等。通知紧急程度有多种类型:FATAL、URGENCY、NORMAL、TRIVIAL。FATAL对应电话报警、URGENCY对应微信报警、NORMAL对应短信报警、TRIVI

2021-08-03 11:17:09 100

原创 一文搞懂代理模式

原理不改变原始类代码的情况下,通过引入代理类给原始类附加功能应用场景开发一些非业务功能比如统计接口耗时、记录日志、监控、限流、鉴权等非业务功能我们可以采用代理模式将这些非业务功能写在代理类,和业务功能类隔离开,各司其职在RPC中的应用RPC中使用了动态代理,将编解码、序列化、网络传输等非业务功能封装在代理类中,程序员只需要调用业务功能原始类的方法,关注业务逻辑即可在缓存中的应用比如我们要开发一个接口请求的缓存功能,对于某些接口,如果入参相同且在设定的有效期内,那么我们直接返回缓存的结果,这个

2021-08-02 10:54:45 91

原创 一文搞懂原型模式

原理可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新对象,以达到节省创建时间的目的应用场景如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下可以使用原型模式创建新对象何为创建成本大?如果对象中的数据需要经过复杂的计算才能得到(比如排序、计算哈希值),或者需要从 RPC、网络、数据库、文件系统等非常慢速的 IO 中读取,这种情况下,我们就可以利用原型模式,从其他已有对象中直接拷贝得到,而不用每次在创建新对象的时候,都重复执行这些耗时的操作。实

2021-07-27 11:10:39 72

原创 一文搞懂建造者模式

原理创建对象需要的属性以及需要校验的属性,那么建造者就配置同样的属性;创建建造者,通过set方法设置建造者的属性,使用build方法创建对象,build方法创建对象前对参数进行一系列校验应用场景当创建对象的必填参数非常多,如果此时采用New方法创建,那么必然会导致New方法的参数很多,是一个庞大的函数,使用时可能会出现参数传递乱序的问题导致隐藏BUG,此时采用建造者模式就不需要冗长的New函数,并且可以在Build函数中对必填参数进行非空的校验当参数之间存在依赖关系时,比如一个参数如果有值,那么另

2021-07-26 10:45:57 97

原创 一文搞懂工厂模式

原理工厂模式分为三种:简单工厂模式、工厂方法模式、抽象工厂模式,前两种比较简单也比较常用,抽象工厂比较复杂也不常用简单工厂原理:一个创建方法创建多个类型(继承相同父类或实现相同接口)的对象工厂方法原理:一个创建方法创建一个类型对象抽象工厂原理:多个创建方法创建多个类型的对象,一个方法只创建一个类型对象应用场景应用场景:如果对象的创建比较复杂,那么就适合使用工厂模式创建对象,将对象的创建和使用解耦对象创建复杂情况:1. 需要根据不同的类型创建不同的对象,存在if、else逻辑,这个时候可以把对

2021-07-23 11:16:24 131

原创 一文搞懂单例模式

原理一个类只允许创建一个对象,那么这个类就是单例类代码实现饿汉式在程序启动时创建唯一对象var loggerCreateOnce sync.Oncevar singleLogger *Loggerfunc newSingleLogger() { filePath := "/log.txt" f, _ := os.Create(filePath) writer := bufio.NewWriter(f) singleLogger = &Logger{fileWriter: wr

2021-07-19 10:58:35 116

原创 Hot100系列(494. 目标和)—Go版本

思路:每个数字都有两种可能,+或者-,那么直接递归遍历每个数,扫描出所有可能var findTargetSumWaysRes intfunc FindTargetSumWays(nums []int, target int) int { recursiveTargetSumWays(0, nums, 0, target) returnR := findTargetSumWaysRes findTargetSumWaysRes = 0 return returnR}func recursi

2021-07-13 14:09:42 74

原创 Hot100系列(461. 汉明距离)—Go版本

思路:x&1 求x的第一位,y&1 求y的第一位,进行比较如果不相同那么res+1x >> 1, y >> 1 继续&1进行比较func HammingDistance(x int, y int) int { res := 0 for x != 0 || y != 0 { if x&1 != y&1 { res++ } x >>= 1 y >>= 1 } return res}

2021-07-13 14:07:54 79

原创 常用限流算法—Go语言版本

滑动窗口相对固定窗口的改进比如窗口大小是1S,限制是1S内最多100个请求如果是固定窗口前一S的最后250ms出现100个请求,后一S的前250ms出现100个请求,对于固定窗口来说是符合规定的,但此时就出现了边界峰值,1S内有200个请求,会压垮后端系统如果是滑动窗口假设滑动窗口将整个1S的大窗口分为4个小的窗口(timeSlot),每过250ms大窗口向后滑动,抛弃首个timeSlot,尾部新增一个timeSlot,统计整个大窗口所有timeSlot的请求数此时,前一S的最后250ms出.

2021-07-12 11:06:54 293 1

原创 Hot100系列(438. 找到字符串中所有字母异位词)—Go版本

思路:滑动窗口+双指针使用pMap统计p中所有字母及出现次数,使用sMap统计s窗口内的字母及出现次数left、right初始化为0,right开始向右遍历,如果right-left+1的长度和p相同,此时比较pMap、sMap是否相同,如果相同,那么添加left到res中,此时left+1向右移动(即代表窗口向右移动一格)import ( "encoding/json" "reflect")func FindAnagrams(s string, p string) []int { re

2021-07-10 22:31:49 79

原创 Linux的IO多路复用

IO多路复用多路:多个文件描述符fd复用:复用一个后台线程多路复用就是指:复用一个后台线程处理多个fdIO多路复用的目的:快速处理每个fd那一个 IO 请求(比如 write )对应一个线程来处理,这样所有的 IO 不都并发了吗?是可以,但是有瓶颈,线程数一旦多了,性能是反倒会差的最朴素的实现while True: for each fdList { read/write(fd, /* 参数 */) } sleep(1s)默认创建的fd都是阻塞型的,

2021-07-09 11:42:45 232

原创 Hot100系列(437. 路径总和 III)—Go版本

思路:根左右递归遍历每个节点,每个节点都当做根节点进行根左右递归遍历看是否存在路径和为target的路劲,有的话则res+1var res intvar target intfunc pathSum(root *TreeNode, targetSum int) int { target = targetSum dfsTree(root) returnR := res res = 0 target = 0 return returnR}func dfsTree(root *TreeN

2021-07-07 19:52:17 72

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除