从PHP 到Golang 的笔记 ( 转 )
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从PHP 到Golang 的笔记 ( 转 )相关的知识,希望对你有一定的参考价值。
参考技术A———文章来源 YamiOdymel/php-to-Golang
PHP和模块之间的关系令人感到烦躁,假设你要读取 yaml 档案,你需要有一个 yaml 的模块,为此,你还需要将其编译然后将编译后的模块摆放至指定位置,之后换了一台伺服器你还要重新编译,这点到现在还是没有改善;顺带一提之后出了PHP 7效能确实提升了许多(比Python 3快了些),但PHP仍令我感到臃肿,我觉得是时候
(转行)了。
PHP 和Golang 的效能我想毋庸置疑是后者比较快(而且是以倍数来算),也许有的人会认为两种不应该被放在一起比较,但Golang 本身就是偏向Web 开发的,所以这也是为什么我考虑转用Golang 的原因,起初我的考虑有几个:Node.js 和Rust 还有最终被选定的Golang;先谈谈Node.js 吧。
Node.js的效能可以说是快上PHP 3.5倍至6倍左右 ,而且撰写的语言还是javascript,蒸蚌,如此一来就不需要学习新语言了!搭配Babel更可以说是万能,不过那跟「跳跳虎」一样的Async逻辑还有那恐怖的Callback Hell,有人认为前者是种优点,这点我不否认,但是对学习PHP的我来说太过于"Mind Fuck",至于后者的Callback Hell虽然有Promise,但是那又是另一个「Then Hell」的故事了。相较于Golang之下,Node.js似乎就没有那么吸引我了。你确实可以用Node.js写出很多东西,不过那V8引擎的效能仍然有限,而且要学习新的事物,不就应该是「全新」的吗;)?
题外话: 为什么Node.js不适合大型和商业专案?
在抛弃改用Node.js 之后我曾经花了一天的时间尝试Rust 和Iron 框架,嗯⋯⋯Rust 太强大了,强大到让我觉得Rust 不应该用在这里,这想法也许很蠢,但Rust 让我觉得适合更应该拿来用在系统或者是部分底层的地方,而不应该是网路服务。
Golang是我最终的选择,主要在于我花了一天的时间来研究的时候意外地发现Golang夭寿简洁( 关键字只有25个 ),相较之下Rust太过于「强大」令我怯步;而且Golang带有许多工具,例如 go fmt 会自动帮你整理程式码、 go doc 会自动帮你生产文件、 go test 可以自动单元测试并生产覆盖率报表、也有 go get 套件管理工具(虽然没有版本功能),不过都很实用,而且也不需要加上分号( ; ),真要说不好的地方⋯⋯大概就是强迫你花括号不能换行放吧(没错,我就是花括号会换行放的人)。
当我在撰写这份文件的时候 我会先假设你有一定的基础 ,你可以先阅读下列的手册,他们都很不错。
你能够在PHP 里面想建立一个变数的时候就直接建立,夭寿赞,是吗?
蒸蚌!那么Golang 呢?在Golang 中变数分为几类:「新定义」、「预先定义」、「自动新定义」、「覆盖」。让我们来看看范例:
在PHP中你会很常用到 echo 来显示文字,像这样。
然而在Golang中你会需要 fmt 套件,关于「什么是套件」的说明你可以在文章下述了解。
这很简单,而且两个语言的用法相差甚少,下面这是PHP:
只是Golang 稍微聒噪了一点,你必须在函式后面宣告他最后会回传什么资料型别。
在PHP 中你要回传多个资料你就会用上阵列,然后将资料放入阵列里面,像这样。
然而在Golang 中你可以不必用到一个阵列,函式可以一次回传多个值:
两个语言的撰写方式不尽相同。
主要是PHP 的阵列能做太多事情了,所以在PHP 里面要储存什么用阵列就好了。
在Golang里⋯⋯没有这么万能的东西,首先要先了解Golang中有这些型态: array , slice , map , interface ,
你他妈的我到底看了三洨,首先你要知道Golang是个强型别语言,意思是你的阵列中 只能有一种型态 ,什么意思?当你决定这个阵列是用来摆放字串资料的时候,你就只能在里面放字串。没有数值、没有布林值,就像你没有女朋友一样。
先撇开PHP 的「万能阵列」不管,Golang 中的阵列既单纯却又十分脑残,在定义一个阵列的时候,你必须给他一个长度还有其内容存放的资料型态,你的阵列内容不一定要填满其长度,但是你的阵列内容不能超过你当初定义的长度。
切片⋯⋯这听起来也许很奇怪,但是你确实可以「切」他,让我们先谈谈「切片」比起「阵列」要好在哪里:「你不用定义其最大长度,而且你可以直接赋予值」,没了。
我们刚才有提到你可以「切」他,记得吗?这有点像是PHP中的 array_slice() ,但是Golang直接让Slice「内建」了这个用法,其用法是: slice[开始:结束] 。
在PHP中倒是没有那么方便,在下列PHP范例中你需要不断地使用 array_slice() 。
你可以把「映照」看成是一个有键名和键值的阵列,但是记住:「你需要事先定义其键名、键值的资料型态」,这仍限制你没办法在映照中存放多种不同型态的资料。
在Golang里可就没这么简单了,你需要先用 make() 宣告 map 。
也许你不喜欢「接口」这个词,但用「介面」我怕会误导大众,所以,是的,接下来我会继续称其为「接口」。还记得你可以在PHP 的关联阵列里面存放任何型态的资料吗,像下面这样?
现在你有福了!正因为Golang中的 interface 可以接受任何内容,所以你可以把它拿来存放任何型态的资料。
有时候你也许会有个不定值的变数,在PHP 里你可以直接将一个变数定义成字串、数值、空值、就像你那变心的女友一样随时都在变。
在Golang中你必须给予变数一个指定的资料型别,不过还记得刚才提到的:「Golang中有个 interface 能够 存放任何事物 」吗( 虽然也不是真的任何事物啦⋯⋯ )?
当我们程式中不需要继续使用到某个资源或是发生错误的时候,我们索性会将其关闭或是抛弃来节省资源开销,例如PHP 里的读取档案:
在Golang中,你可以使用 defer 来在函式结束的时候自动执行某些程式(其执行方向为反向)。所以你就不需要在函式最后面结束最前面的资源。
defer 可以被称为「推迟执行」,实际上就是在函式结束后会「反序」执行的东西,例如你按照了这样的顺序定义 defer : A->B->C->D ,那么执行的顺序其实会是 D->C->B->A ,这用在程式结束时还蛮有用的,让我们看看Golang如何改善上述范例。
这东西很邪恶,不是吗?又不是在写BASIC,不过也许有时候你会在PHP 用上呢。但是拜托,不要。
Golang中仅有 for 一种回圈但却能够达成 foreach 、 while 、 for 多种用法。普通 for 回圈写法在两个语言中都十分相近。
在Golang请记得:如果你的 i 先前并不存在,那么你就需要定义它,所以下面这个范例你会看见 i := 0 。
在PHP里, foreach() 能够直接给你值和键名,用起来十分简单。
Golang里面虽然仅有 for() 但却可以使用 range 达成和PHP一样的 foreach 方式。
一个 while(条件) 回圈在PHP里面可以不断地执行区块中的程式,直到 条件 为 false 为止。
在Golang里也有相同的做法,但仍是透过 for 回圈,请注意这个 for 回圈并没有任何的分号( ; ),而且一个没有条件的 for 回圈会一直被执行。
PHP中有 do .. while() 回圈可以先做区块中的动作。
在Golang中则没有相关函式,但是你可以透过一个无止尽的 for 回圈加上条件式来让他结束回圈。
要是你真的希望完全符合像是PHP那样的设计方式,或者你可以在Golang中使用很邪恶的 goto 。
在PHP中我们可以透过 date() 像这样取得目前的日期。
在Golang就稍微有趣点了,因为Golang中并不是以 Y-m-d 这种格式做为定义,而是 1 、 2 、 3 ,这令你需要去翻阅文件,才能够知道 1 的定义是代表什么。
俗话说:「爆炸就是艺术」,可爱的PHP用词真的很大胆,像是: explode() (爆炸)、 die() (死掉),回归正传,如果你想在PHP里面将字串切割成阵列,你可以这么做。
简单的就让一个字串给「爆炸」了,那么Golang 呢?
对了,记得引用 strings 套件。
这真的是很常用到的功能,就像物件一样有着键名和键值,在PHP 里面你很简单的就能靠阵列(Array)办到。
真是太棒了,那么Golang呢?用 map 是差不多啦。如果有必要的话,你可以稍微复习一下先前提到的「多资料储存型态-Stores」。
你很常会在PHP里面用 isset() 检查一个索引是否存在,不是吗?
在Golang里面很简单的能够这样办到(仅适用于 map )。
指针(有时也做参照)是一个像是「变数别名」的方法,这种方法让你不用整天覆盖旧的变数,让我们假设 A = 1; B = A; 这个时候 B 会复制一份 A 且两者不相干,倘若你希望修改 B 的时候实际上也会修改到 A 的值,就会需要指针。
指针比起复制一个变数,他会建立一个指向到某个变数的记忆体位置,这也就是为什么你改变指针,实际上是在改变某个变数。
在Golang你需要用上 * 还有 & 符号。
有些时候你会回传一个阵列,这个阵列里面可能有资料还有错误代号,而你会用条件式判断错误代号是否非空值。
在Golang中函式可以一次回传多个值。为此,你不需要真的回传一个阵列,不过要注意的是你将会回传一个属于 error 资料型态的错误,所以你需要引用 errors 套件来帮助你做这件事。
该注意的是Golang没有 try .. catch ,因为 Golang推荐这种错误处理方式 ,你应该在每一次执行可能会发生错误的程式时就处理错误,而非后来用 try 到处包覆你的程式。
在 if 条件式里宣告变数会让你只能在 if 内部使用这个变数,而不会污染到全域范围。
也许你在PHP中更常用的会是 try .. catch ,在大型商业逻辑时经常看见如此地用法,实际上这种用法令人感到聒噪(因为你会需要一堆 try 区块):
Golang中并没有 try .. catch ,实际上Golang也 不鼓励这种行为 (Golang推荐逐一处理错误的方式),倘若你真想办倒像是捕捉异常这样的方式,你确实可以使用Golang中另类处理错误的方式(可以的话尽量避免使用这种方式): panic() , recover() , defer 。
你可以把 panic() 当作是 throw (丢出错误),而这跟PHP的 exit() 有87%像,一但你执行了 panic() 你的程式就会宣告而终,但是别担心,因为程式结束的时候会呼叫 defer ,所以我们接下来要在 defer 停止 panic() 。
关于 defer 上述已经有提到了,他是一个反向执行的宣告,会在函式结束后被执行,当你呼叫了 panic() 结束程式的时候,也就会开始执行 defer ,所以我们要在 defer 内使用 recover() 让程式不再继续进行结束动作,这就像是捕捉异常。
recover() 可以看作 catch (捕捉),我们要在 defer 里面用 recover() 解决 panic() ,如此一来程式就会回归正常而不会被结束。
还记得在PHP里要引用一堆档案的日子吗?到处可见的 require() 或是 include() ?到了Golang这些都不见了,取而代之的是「套件(Package)」。现在让我们来用PHP解释一下。
这看起来很正常对吧?但假设你有一堆档案,这马上就成了 Include Hell ,让我们看看Golang怎么透过「套件」解决这个问题。
「 蛤???杀小??? 」你可能如此地说道。是的, main.go 中除了引用 fmt 套件( 为了要输出结果用的套件 )之外完全没有引用到 a.go 。
「 蛤???杀小?????? 」你仿佛回到了几秒钟前的自己。
既然没有引用其他档案,为什么 main.go 可以输出 foo 呢?注意到了吗, 两者都是属于 main 套件 ,因此 他们共享同一个区域 ,所以接下来要介绍的是什么叫做「套件」。
套件是每一个 .go 档案都必须声明在Golang原始码中最开端的东西,像下面这样:
这意味着目前的档案是属于 main 套件( 你也可以依照你的喜好命名 ),那么要如何让同个套件之间的函式沟通呢?
接着是Golang;注意!你不需要引用任何档案,因为下列两个档案同属一个套件。
一个由「套件」所掌握的世界,比起PHP的 include() 和 require() 还要好太多了,对吗?
在Golang 中没有引用单独档案的方式,你必须汇入一整个套件,而且你要记住:「一定你汇入了,你就一定要使用它」,像下面这样。
假如你不希望使用你汇入的套件,你只是为了要触发那个套件的 main() 函式而引用的话⋯⋯,那么你可以在前面加上一个底线( _ )。
如果你的套件出现了名称冲突,你可以在套件来源前面给他一个新的名称。
现在你知道可以汇入套件了,那么什么是「汇出」?同个套件内的函式还有共享变数确实可以直接用,但那 并不表示可以给其他套件使用 ,其方法取决于 函式/变数的「开头大小写」 。
是的。 Golang依照一个函式/变数的开头大小写决定这个东西是否可供「汇出」 。
这用在区别函式的时候格外有用,因为小写开头的任何事物都是不供汇出的,反之,大写开头的任何事物都是用来汇出供其他套件使用的。
一开始可能会觉得这是什么奇异的规定,但写久之后,你就能发现比起JavaScript和Python以「底线为开头的命名方式」还要来得更好;比起成天宣告 public 、 private 、 protected 还要来得更快。
在Golang 中没有类别,但有所谓的「建构体(Struct)」和「接口(Interface)」,这就能够满足几乎所有的需求了,这也是为什么我认为Golang 很简洁却又很强大的原因。
让我们先用PHP 建立一个类别,然后看看Golang 怎么解决这个问题。
虽然Golang没有类别,但是「建构体(Struct)」就十分地堪用了,首先你要知道在Golang中「类别」的成员还有方法都是在「类别」外面所定义的,这跟PHP在类别内定义的方式有所不同,在Golang中还有一点,那就是他们没有 public 、 private 、 protected 的种类。
在PHP中,当有一个类别被 new 的时候会自动执行该类别内的建构子( __construct() ),通常你会用这个来初始化一些类别内部的值。
但是在Golang 里因为没有类别,也就没有建构子,不巧的是建构体本身也不带有建构子的特性,这个时候你只能自己在外部建立一个建构用函式。
让我们假设你有两个类别,你会把其中一个类别传入到另一个类别里面使用,废话不多说!先上个PHP 范例(为了简短篇幅我省去了换行)。
在Golang中你也有相同的用法,但是请记得:「 任何东西都是在「类别」外完成建构的 」。
在PHP 中没有相关的范例,这部分会以刚才「嵌入」章节中的Golang 范例作为解说对象。
你可以看见Golang在进行 Foo 嵌入 Bar 的时候,会自动将 Foo 的成员暴露在 Bar 底下,那么假设「双方之间有相同的成员名称」呢?
这个时候被嵌入的成员就会被「遮蔽」,下面是个实际范例,还有你如何解决遮蔽问题:
虽然都是呼叫同一个函式,但是这个函式可以针对不同的资料来源做出不同的举动,这就是多形。你也能够把这看作是:「讯息的意义由接收者定义,而不是传送者」。
目前PHP 中没有真正的「多形」,不过你仍可以做出同样的东西。
嗯⋯⋯那么Golang呢?实际上更简单而且更有条理了,在Golang中有 interface 可以帮忙完成这个工作。
如果你对Interface还不熟悉,可以试着查看「 解释Golang中的Interface到底是什么 」文章。
谢谢你看到这里,可惜这篇文章却没有说出Golang 最重要的卖点:「Goroutine」和「Channel」
从php到Golang系统的演变
前言
工作这些年,先后经历过两家公司,有参与过php语言框架的开发和主导过go语言技术栈的落地工作,在此过程中有一些感悟和总结。我想以之前我主导的go语言技术栈为线索,来陈述当时遇到的一些问题,以及分析问题和解决问题的思路。主要目的是想陈述go技术体系在团队中落地的过程,分析我们在各个阶段中,遇到的一些问题,并将分析问题的思路和解决问题的方法记录下来,以便让后来的同学了解go语言在团队的演进过程,吸取相关的经验,以便在今后的系统设计和开发上少走弯路。
在系统不断演进的过程中,有时候对框架的选型很随意,认为能满足现在功能就行,没有对其功能扩展性和性能进行考量,导致随着业务的发展,发现当时选型有误,但想转又很难。那么现在,我就来谈谈,我们是如何抉择这些事情的。
我们为什么要由php转向go
最初,大约是在2015年时,平台内所有的业务系统均是由php语言构成的,上线没多久,平台的流量开始爆发性增长,并发量越来越大,晚高峰每秒请求由最初的几千QPS到现在的几十万QPS。当时,最快最有效的优化手段无外乎加机器和增加php-fpm的数量,但是,受限于php本身的网络模型,终究不适合这种高并发,大流量的场景。面对这种棘手的问题,再加上当时人手有限,业务任务重等因素,于是找其它部门借了一批写lua的外援,帮忙把一些逻辑简单且访问量大的接口,换成了lua,暂时扛过了晚高峰,因此,有相当长的一段时间,大部分项目是php+lua共存的一个状态。但由于lua本身的一些局限性,不太适合做一些复杂的业务逻辑,所以最终,在业务有强烈的需求的前提下,同时伴随着技术发展的潮流,于2015年底,我们开始选择转向go语言。
我们怎样由php转向go
由于之前团队全部都是php栈,在go方面的积累并不多,所以在php转向go的过程中,面临了在转型过程中都会遇到的问题:
1 用什么框架;
2 在业务任务重,人员极其匮乏的情况下如何將php项目重构成go。
用什么框架
之前团队有人仿造内部php框架开发过一个golang框架,有人提议将其直接拿过来用,有人说找个开源的如beego,gin,martini等这类流行的框架。我个人当时不太赞同使用自研的框架,主要有以下几点原因:1 文档少,漏洞多;2 需要投入人力去开发和维护,在当时人力极其紧缺的情况下是不现实的。另外,当时社区流行的框架也比较多,但是最终也没有选择那些流行的框架,主要是出于以下考虑:时间短,任务重,没有精力去辨别各个框架的优劣,适用场景以及性能如何。万一冒然使用一个还没有深入了解的框架,线上出问题咋办!尤其在当时系统频繁出问题,顶着各种压力的情况下。
虽说,我们无法在短时间内选一个合适的框架,但是能够确定的是:我们的需求是什么?
1 只做高性能的HTTP 接口;
2 需要完整的单元测试体系;
3 可扩展,组件化;
基于以上三点,可以发现,go语言自带的特性就可以满足这些需求。于是,我们开始决定裸写。
此外,还有一个裸写的原因就是:没想好将来想要什么!当然,每个团队的背景不一样,业务场景也不同,在人力和时间充裕的情况下,还是需要选择一个合适的框架比较好。
裸写不是乱写
裸写不是乱写。众所周知,用框架的其中一个好处就是保证团队代码风格的一致性,当然,目前市面上除了beego外的大多数框架,在代码风格上也并没有做约束。为了保证团队go代码的规范性和一致性,按照经典的分层架构和过往的经验,我们制定了一套go编程模版,由上向下:Router层,Service层,Dao层,还有贯穿这三层的Entity层,架构图如图1所示。其中,Router层负责处理与http handler逻辑,请求参数以及response格式相关的处理工作;Service层处理业务逻辑;Dao层处理数据访问逻辑;Entity层负责实体定义相关的逻辑,并贯穿Router,Service,Dao这三层。层与层之间不直接进行耦合,高层模块不直接依赖与低层模块,它们都依赖于所定义的抽象接口。
Booch曾经说过:“所有结构良好的面向对象构架都具有清晰的层次定义,每个层次通过一个定义良好的,受控的接口向外提供了一组内聚的服务”。
除此之外,我们还维护了一套常用的公共组件库,如:日志库,各种数据库driver等。
图1 分层构架
如何重构
当我们制定好编程模版后,我们就开始进行项目重构工作。由于,业务任务重,人手少,所以,重构的基本方向就是:根据业务需求,结合接口重要性进行重构。只有这样,才能保证在业务需求不停的情况下,进行系统重构。所以,在此期间,有相当长的一段时间是处于php+go进行混合编程,混合部署的状态,如图2所示,重构完的接口,通过nginx代理到新接口,这种状态一直持续了一年。采取混合编程的思路在重构初期,可能会遇到一些问题,比如:同一段业务逻辑,需要用go写一遍,用php写一遍,无疑增加了一定的工作量,当然这也是避免不了的。
注意,有些同学在重构的时候容易走到一个误区:一口气把整个项目都重构了,或者说重构大部分内容。从时间成本和系统稳定性上来讲,这种方式风险比较大,不推荐。推荐的思路:一个接口一个接口进行重构。
图2 php+go混合架构
最终,为何想引入go-kit
之前这套东西,基本上可以满足大部分的业务场景,但随着随着业务的发展,请求量越来越大,同时,有些请求的链路也变长了,为了继续保证接口的高并发和低时延特性,团队有少量业务开始尝试GRPC。根据测试,压测一个空接口,GRPC的性能大约是HTTP+JSON的2~3倍,在这里推荐一个压测框架fperf 。
但是,针对GRPC的使用,不要盲目“求新”。以本人经验,HTTP+JSON的模式基本上可以满足大部分的业务开发场景了,针对小部分对接口时延和并发量要求极高的场景可以考虑使用GRPC。因为,GRPC本身还是不利于调试,且会在一定程度上增加调用方和服务方的耦合性,所以,最后的传输协议和格式建议还是以HTTP+JSON为主,以GRPC为辅。
另外,我们还是需要标准化一些中间件的使用,如回路断流,rate limit等,来保障系统的稳定性。这次的思考,时间比较充分,所以有精力去研究一些新的东西。最后,框架抉择的思路和最初是一样的,就是,明确我们的需求是什么?
1 需要一个同时支持多种传输协议,不论是现在的http,thrift,grpc,还是将来出现的某种新协议,要有良好扩展性的框架;
2 框架本身和业务代码保持一种低耦合的状态;
3 需要一套通用的middleware,使之与http,grpc等传输协议无关。
目前市面上流行的框架都是围绕着http协议而展开的,包括gin,beego等。经调研,我们发现go-kit能够满足我们的需求。go-kit本身不是一个框架,而是一套微服务工具集。其设计思想跟我们初期go编程模版制定的思想也算是不谋而合——分层设计,组件化,可扩展。go-kit的架构如图3所示,分为三层结构:Transport层,Endpoint层,Service层。Transport层主要负责与传输协议HTTP,GRPC,THRIFT等相关的逻辑,Endpoint层主要负责request/response格式的转换,以及公用拦截器相关的逻辑;Service层则专注于业务逻辑。go-kit除了经典的分层架构外,还在endpoint层提供了很多公用的拦截器,如log,metric,tracing,circuitbreaker,rate-limiter等,来保障业务系统的可用性。它们在设计上有一个共同特点:都是同传输协议无关的。在之前的一些http框架中,这些拦截器同传输协议是紧紧耦合在一起的,如果,此时我需要将某些HTTP接口改造成GRPC协议的接口,那么这些拦截器我还得再基于grpc再实现一遍,设计上存在一定的冗余。因此,借助go-kit这套工具集,我们就能很好的对transport协议,middleware进行扩展,且不会影响到业务本身的设计。
图3 go-kit架构图
怎样將go-kit集成到现有的业务系统中
我们找到了心仪的开源工具后,那么我们怎样以较低的成本将其引入到我们业务系统中呢?之前我们有提到,我们的go模版是分为三层:router,service和dao。而go-kit也分为三层,我们可以根据每层职责的不同进行重新组合,如图4所示,从上到下依次为:transport层,endpoint层,service层,dao层。这样就能很轻易的将go-kit集成进来,当然你如果哪天因为某种原因,不想再继续使用go-kit这套东西,直接將endpoint层和Transport层移除即可。在集成的过程中,需要注意一点:之前的代码中router层不能包含任何业务逻辑,否则就无法集成。
图4 架构的演进
如何高效的使用go-kit
前面有提到,go-kit本身分为三层,针对这点有同学会提出:“每次新建项目,都需要手动写下go-kit的这三层逻辑,有点浪费时间,不够简洁”,这确实是一个共性问题,从go-kit的github的issue中可以发现,也有不少人反馈过类似问题。很庆幸的是,有人给我们铺好了路,详见GoKit CLi,其主要功能如图5所示。
图5 GoKit CLi功能模块
这个工具可以根据我们的需求自动生成service,transport和endpoint模版,以及生成供调用方的使用的client library,节省我们大量的时间,提高我们的生产效率。具体操作步骤,可以参考GoKit CLi的说明,这里不再赘述。
总结
不论是我之前的一篇文章《浅谈互联网业务系统设计》所讲的系统设计,还是这篇文章所陈述的框架选型。我一直在强调的一点就是:需求是什么?如何在满足需求的同时,让框架和系统具有一定的弹性。无外乎使用经典的五大设计原则:单一职责原则,开放封闭原则,依赖倒置原则,接口隔离原则,为你的设计提供坚实的理论基础和方向指引。另外,在做选型的时候不要盲从,别人口中好的东西,不一定适合你,只有明确自身需求后,找到适合自己的才是最好的!
来源:
https://www.jianshu.com/p/0c34a75569b1
“IT大咖说”欢迎广大技术人员投稿,投稿邮箱:aliang@itdks.com
IT大咖说 | 关于版权
感谢您对IT大咖说的热心支持!
相关推荐
推荐文章
以上是关于从PHP 到Golang 的笔记 ( 转 )的主要内容,如果未能解决你的问题,请参考以下文章