本著什么原則,才能寫(xiě)出優(yōu)秀的代碼?
作為一名程序員,最不愛(ài)干的事情,除了開(kāi)會(huì)之外,可能就是看別人的代碼。
有的時(shí)候,新接手一個(gè)項(xiàng)目,打開(kāi)代碼一看,要不是身體好的話,可能直接氣到暈厥。
風(fēng)格各異,沒(méi)有注釋,甚至連最基本的格式縮進(jìn)都做不到。這些代碼存在的意義,可能就是為了證明一句話:又不是不能跑。
在這個(gè)時(shí)候,大部分程序員的想法是:這爛代碼真是不想改,還不如直接重寫(xiě)。
但有的時(shí)候,我們看一些著名的開(kāi)源項(xiàng)目時(shí),又會(huì)感嘆,代碼寫(xiě)得真好,優(yōu)雅。為什么好呢?又有點(diǎn)說(shuō)不出來(lái),總之就是好。
那么,這篇文章就試圖分析一下好代碼都有哪些特點(diǎn),以及本著什么原則,才能寫(xiě)出優(yōu)秀的代碼。
初級(jí)階段
先說(shuō)說(shuō)比較基本的原則,只要是程序員,不管是高級(jí)還是初級(jí),都會(huì)考慮到的。

這只是列舉了一部分,還有很多,我挑選四項(xiàng)簡(jiǎn)單舉例說(shuō)明一下。
格式統(tǒng)一 命名規(guī)范 注釋清晰 避免重復(fù)代碼
以下用 Python 代碼分別舉例說(shuō)明:
格式統(tǒng)一
格式統(tǒng)一包括很多方面,比如 import 語(yǔ)句,需要按照如下順序編寫(xiě):
Python 標(biāo)準(zhǔn)庫(kù)模塊 Python 第三方模塊 應(yīng)用程序自定義模塊
然后每部分間用空行分隔。
import?os
import?sys
import?msgpack
import?zmq
import?foo
再比如,要添加適當(dāng)?shù)?strong style="line-height: 1.75em;">空格,像下面這段代碼;
i=i+1
submitted?+=1
x?=?x*2?-?1
hypot2?=?x*x?+?y*y
c?=?(a+b)?*?(a-b)
代碼都緊湊在一起了,很影響閱讀。
i?=?i?+?1
submitted?+=?1
x?=?x?*?2?-?1
hypot2?=?x?*?x?+?y?*?y
c?=?(a?+?b)?*?(a?-?b)
添加空格之后,立刻感覺(jué)清晰了很多。
還有就是像 Python 的縮進(jìn),其他語(yǔ)言的大括號(hào)位置,是放在行尾,還是另起新行,都需要保證統(tǒng)一的風(fēng)格。
有了統(tǒng)一的風(fēng)格,會(huì)讓代碼看起來(lái)更加整潔。
命名規(guī)范
好的命名是不需要注釋的,只要看一眼命名,就能知道變量或者函數(shù)的作用。
比如下面這段代碼:
a?=?'zhangsan'
b?=?0
a 可能還能猜到,但當(dāng)代碼量大的時(shí)候,如果滿屏都是 a,b,c,d,那還不得原地爆炸。
把變量名稍微改一下,就會(huì)使語(yǔ)義更加清晰:
username?=?'zhangsan'
count?=?0
還有就是命名要風(fēng)格統(tǒng)一。如果用駝峰就都用駝峰,用下劃線就都用下劃線,不要有的用駝峰,有點(diǎn)用下劃線,看起來(lái)非常分裂。
注釋清晰
看別人代碼的時(shí)候,最大的愿望就是注釋清晰,但在自己寫(xiě)代碼時(shí),卻從來(lái)不寫(xiě)。
但注釋也不是越多越好,我總結(jié)了以下幾點(diǎn):
注釋不限于中文或英文,但最好不要中英文混用 注釋要言簡(jiǎn)意賅,一兩句話把功能說(shuō)清楚 能寫(xiě)文檔注釋?xiě)?yīng)該盡量寫(xiě)文檔注釋 比較重要的代碼段,可以用雙等號(hào)分隔開(kāi),突出其重要性
舉個(gè)例子:
#?=====================================
#?非常重要的函數(shù),一定謹(jǐn)慎使用?!!!
#?=====================================
def?func(arg1,?arg2):
????"""在這里寫(xiě)函數(shù)的一句話總結(jié)(如:?計(jì)算平均值).
????這里是具體描述.
????參數(shù)
????----------
????arg1?:?int
????????arg1的具體描述
????arg2?:?int
????????arg2的具體描述
????返回值
????-------
????int
????????返回值的具體描述
????參看
????--------
????otherfunc?:?其它關(guān)聯(lián)函數(shù)等...
????示例
????--------
????示例使用doctest格式,?在`>>>`后的代碼可以被文檔測(cè)試工具作為測(cè)試用例自動(dòng)運(yùn)行
????>>>?a=[1,2,3]
????>>>?print?[x?+?3?for?x?in?a]
????[4,?5,?6]
????"""
避免重復(fù)代碼
隨著項(xiàng)目規(guī)模變大,開(kāi)發(fā)人員增多,代碼量肯定也會(huì)增加,避免不了的會(huì)出現(xiàn)很多重復(fù)代碼,這些代碼實(shí)現(xiàn)的功能是相同的。
雖然不影響項(xiàng)目運(yùn)行,但重復(fù)代碼的危害是很大的。最直接的影響就是,出現(xiàn)一個(gè)問(wèn)題,要改很多處代碼,一旦漏掉一處,就會(huì)引發(fā) BUG。
比如下面這段代碼:
import?time
def?funA():
????start?=?time.time()
????for?i?in?range(1000000):
????????pass
????end?=?time.time()
????print("funA?cost?time?=?%f?s"?%?(end-start))
def?funB():
????start?=?time.time()
????for?i?in?range(2000000):
????????pass
????end?=?time.time()
????print("funB?cost?time?=?%f?s"?%?(end-start))
if?__name__?==?'__main__':
????funA()
????funB()
funA() 和 funB() 中都有輸出函數(shù)運(yùn)行時(shí)間的代碼,那么就適合將這些重復(fù)代碼抽象出來(lái)。
比如寫(xiě)一個(gè)裝飾器:
def?warps():
????def?warp(func):
????????def?_warp(*args,?**kwargs):
????????????start?=?time.time()
????????????func(*args,?**kwargs)
????????????end?=?time.time()
????????????print("{}?cost?time?=?{}".format(getattr(func,?'__name__'),?(end-start)))
????????return?_warp
????return?warp
這樣,通過(guò)裝飾器方法,實(shí)現(xiàn)了同樣的功能。以后如果需要修改的話,直接改裝飾器就好了,一勞永逸。
進(jìn)階階段
當(dāng)代碼寫(xiě)時(shí)間長(zhǎng)了之后,肯定會(huì)對(duì)自己有更高的要求,而不只是格式,注釋這些基本規(guī)范。
但在這個(gè)過(guò)程中,也是有一些問(wèn)題需要注意的,下面就來(lái)詳細(xì)說(shuō)說(shuō)。
炫技
第一個(gè)要說(shuō)的就是「炫技」,當(dāng)對(duì)代碼越來(lái)越熟悉之后,總想寫(xiě)一些高級(jí)用法。但現(xiàn)實(shí)造成的結(jié)果就是,往往會(huì)使代碼過(guò)度設(shè)計(jì)。
這不得不說(shuō)說(shuō)我的親身經(jīng)歷了,曾經(jīng)有一段時(shí)間,我特別迷戀各種高級(jí)用法。
有一次寫(xiě)過(guò)一段很長(zhǎng)的 SQL,而且很復(fù)雜,里面甚至還包含了一個(gè)遞歸調(diào)用。有「炫技」嫌疑的 Python 代碼就更多了,往往就是一行代碼包含了 N 多魔術(shù)方法。
然后在寫(xiě)完之后漏出滿意的笑容,感慨自己技術(shù)真牛。
結(jié)果就是各種被罵,更重要的是,一個(gè)星期之后,自己都看不懂了。

其實(shí),代碼并不是高級(jí)方法用的越多就越牛,而是要找到最適合的。
越簡(jiǎn)單的代碼,越清晰的邏輯,就越不容易出錯(cuò)。而且在一個(gè)團(tuán)隊(duì)中,你的代碼并不是你一個(gè)人維護(hù),降低別人閱讀,理解代碼的成本也是很重要的。
脆弱
第二點(diǎn)需要關(guān)注的是代碼的脆弱性,是否細(xì)微的改變就可能引起重大的故障。
代碼里是不是充滿了硬編碼?如果是的話,則不是優(yōu)雅的實(shí)現(xiàn)。很可能導(dǎo)致每次性能優(yōu)化,或者配置變更就需要修改源代碼。甚至還要重新打包,部署上線,非常麻煩。
而把這些硬編碼提取出來(lái),設(shè)計(jì)成可配置的,當(dāng)需要變更時(shí),直接改一下配置就可以了。
再來(lái),對(duì)參數(shù)是不是有校驗(yàn)?或者容錯(cuò)處理?假如有一個(gè) API 被第三方調(diào)用,如果第三方?jīng)]按要求傳參,會(huì)不會(huì)導(dǎo)致程序崩潰?
舉個(gè)例子:
page?=?data['page']
size?=?data['size']
這樣的寫(xiě)法就沒(méi)有下面的寫(xiě)法好:
page?=?data.get('page',?1)
size?=?data.get('size',?10)
繼續(xù),項(xiàng)目中依賴的庫(kù)是不是及時(shí)升級(jí)更新了?
積極,及時(shí)的升級(jí)可以避免跨大版本升級(jí),因?yàn)榭绱蟀姹旧?jí)往往會(huì)帶來(lái)很多問(wèn)題。
還有就是在遇到一些安全漏洞時(shí),升級(jí)是一個(gè)很好的解決辦法。
最后一點(diǎn),單元測(cè)試完善嗎?覆蓋率高嗎?
說(shuō)實(shí)話,程序員喜歡寫(xiě)代碼,但往往不喜歡寫(xiě)單元測(cè)試,這是很不好的習(xí)慣。
有了完善,覆蓋率高的單元測(cè)試,才能提高項(xiàng)目整體的健壯性,才能把因?yàn)樾薷拇a帶來(lái)的 BUG 的可能性降到最低。
重構(gòu)
隨著代碼規(guī)模越來(lái)越大,重構(gòu)是每一個(gè)開(kāi)發(fā)人員都要面對(duì)的功課,Martin Fowler 將其定義為:在不改變軟件外部行為的前提下,對(duì)其內(nèi)部結(jié)構(gòu)進(jìn)行改變,使之更容易理解并便于修改。
重構(gòu)的收益是明顯的,可以提高代碼質(zhì)量和性能,并提高未來(lái)的開(kāi)發(fā)效率。
但重構(gòu)的風(fēng)險(xiǎn)也很大,如果沒(méi)有理清代碼邏輯,不能做好回歸測(cè)試,那么重構(gòu)勢(shì)必會(huì)引發(fā)很多問(wèn)題。
這就要求在開(kāi)發(fā)過(guò)程中要特別注重代碼質(zhì)量。除了上文提到的一些規(guī)范之外,還要注意是不是濫用了面向?qū)ο缶幊淘瓌t,接口之間設(shè)計(jì)是不是過(guò)度耦合等一系列問(wèn)題。
那么,在開(kāi)發(fā)過(guò)程中,有沒(méi)有一個(gè)指導(dǎo)性原則,可以用來(lái)規(guī)避這些問(wèn)題呢?
當(dāng)然是有的,接著往下看。
高級(jí)階段
最近剛讀完一本書(shū),Bob 大叔的《架構(gòu)整潔之道》,感覺(jué)還是不錯(cuò)的,收獲很多。

全書(shū)基本上是在描述軟件設(shè)計(jì)的一些理論知識(shí)。大體分成三個(gè)部分:編程范式(結(jié)構(gòu)化編程、面向?qū)ο缶幊毯秃瘮?shù)式編程),設(shè)計(jì)原則(主要是 SOLID),以及軟件架構(gòu)(其中講了很多高屋建翎的內(nèi)容)。
總體來(lái)說(shuō),這本書(shū)中的內(nèi)容可以讓你從微觀(代碼層面)和宏觀(架構(gòu)層面)兩個(gè)層面對(duì)整個(gè)軟件設(shè)計(jì)有一個(gè)全面的了解。
其中 SOLID 就是指面向?qū)ο缶幊毯兔嫦驅(qū)ο笤O(shè)計(jì)的五個(gè)基本原則,在開(kāi)發(fā)過(guò)程中適當(dāng)應(yīng)用這五個(gè)原則,可以使軟件維護(hù)和系統(tǒng)擴(kuò)展都變得更容易。
五個(gè)基本原則分別是:
單一職責(zé)原則(SRP) 開(kāi)放封閉原則(OCP) 里氏替換原則(LSP) 接口隔離原則(ISP) 依賴倒置原則(DIP)
單一職責(zé)原則(SRP)
A class should have one, and only one, reason to change. – Robert C Martin
一個(gè)軟件系統(tǒng)的最佳結(jié)構(gòu)高度依賴于這個(gè)系統(tǒng)的組織的內(nèi)部結(jié)構(gòu),因此每個(gè)軟件模塊都有且只有一個(gè)需要被改變的理由。
這個(gè)原則非常容易被誤解,很多程序員會(huì)認(rèn)為是每個(gè)模塊只能做一件事,其實(shí)不是這樣。
舉個(gè)例子:
假如有一個(gè)類 T,包含兩個(gè)函數(shù),分別是 A() 和 B(),當(dāng)有需求需要修改 A() 的時(shí)候,但卻可能會(huì)影響 B() 的功能。
這就不是一個(gè)好的設(shè)計(jì),說(shuō)明 A() 和 B() 耦合在一起了。
開(kāi)放封閉原則(OCP)
Software entities should be open for extension, but closed for modification. – Bertrand Meyer, Object-Oriented Software Construction
如果軟件系統(tǒng)想要更容易被改變,那么其設(shè)計(jì)就必須允許新增代碼來(lái)修改系統(tǒng)行為,而非只能靠修改原來(lái)的代碼。
通俗點(diǎn)解釋就是設(shè)計(jì)的類對(duì)擴(kuò)展是開(kāi)放的,對(duì)修改是封閉的,即可擴(kuò)展,不可修改。
看下面的代碼示例,可以簡(jiǎn)單清晰地解釋這個(gè)原則。
void?DrawAllShape(ShapePointer?list[],?int?n)
{
????int?i;
????for?(i?=?0;?i?????{
????????struct?Shape*?s?=?list[i];
????????switch?(s->itsType)
????????{
????????????case?square:
????????????????DrawSquare((struct?Square*)s);
????????????????break;
????????????case?circle:
????????????????DrawSquare((struct?Circle*)s);
????????????????break;
????????????default:
????????????????break;
????????}
????}
}
上面這段代碼就沒(méi)有遵守 OCP 原則。
假如我們想要增加一個(gè)三角形,那么就必須在 switch 下面新增一個(gè) case。這樣就修改了源代碼,違反了 OCP 的封閉原則。
缺點(diǎn)也很明顯,每次新增一種形狀都需要修改源代碼,如果代碼邏輯復(fù)雜的話,發(fā)生問(wèn)題的概率是相當(dāng)高的。
class?Shape
{
????public:
????????virtual?void?Draw()?const?=?0;
}
class?Square:?public?Shape
{
????public:
????????virtual?void?Draw()?const;
}
class?Circle:?public?Shape
{
????public:
????????virtual?void?Draw()?const;
}
void?DrawAllShapes(vector&?list)
{
????vector::iterator?I;
????for?(i?=?list.begin():?i?!=?list.end();?i++)
????{
????????(*i)->Draw();
????}
}
通過(guò)這樣修改,代碼就優(yōu)雅了很多。這個(gè)時(shí)候如果需要新增一種類型,只需要增加一個(gè)繼承 Shape 的新類就可以了。完全不需要修改源代碼,可以放心擴(kuò)展。
里氏替換原則(LSP)
Require no more, promise no less.– Jim Weirich
這項(xiàng)原則的意思是如果想用可替換的組件來(lái)構(gòu)建軟件系統(tǒng),那么這些組件就必須遵守同一個(gè)約定,以便讓這些組件可以相互替換。
里氏替換原則可以從兩方面來(lái)理解:
第一個(gè)是繼承。如果繼承是為了實(shí)現(xiàn)代碼重用,也就是為了共享方法,那么共享的父類方法就應(yīng)該保持不變,不能被子類重新定義。
子類只能通過(guò)新添加方法來(lái)擴(kuò)展功能,父類和子類都可以實(shí)例化,而子類繼承的方法和父類是一樣的,父類調(diào)用方法的地方,子類也可以調(diào)用同一個(gè)繼承得來(lái)的,邏輯和父類一致的方法,這時(shí)用子類對(duì)象將父類對(duì)象替換掉時(shí),當(dāng)然邏輯一致,相安無(wú)事。
第二個(gè)是多態(tài),而多態(tài)的前提就是子類覆蓋并重新定義父類的方法。
為了符合 LSP,應(yīng)該將父類定義為抽象類,并定義抽象方法,讓子類重新定義這些方法。當(dāng)父類是抽象類時(shí),父類就是不能實(shí)例化,所以也不存在可實(shí)例化的父類對(duì)象在程序里,也就不存在子類替換父類實(shí)例(根本不存在父類實(shí)例了)時(shí)邏輯不一致的可能。
舉個(gè)例子,看下面這段代碼:
class?A{
?public?int?func1(int?a,?int?b){
??return?a?-?b;
?}
}
?
public?class?Client{
?public?static?void?main(String[]?args){
??A?a?=?new?A();
??System.out.println("100-50="?+?a.func1(100,?50));
??System.out.println("100-80="?+?a.func1(100,?80));
?}
}
輸出;
100-50=50
100-80=20
現(xiàn)在,我們新增一個(gè)功能:完成兩數(shù)相加,然后再與 100 求和,由類 B 來(lái)負(fù)責(zé)。即類 B 需要完成兩個(gè)功能:
兩數(shù)相減 兩數(shù)相加,然后再加 100
現(xiàn)在代碼變成了這樣:
class?B?extends?A{
?public?int?func1(int?a,?int?b){
??return?a?+?b;
?}
?
?public?int?func2(int?a,?int?b){
??return?func1(a,b)?+?100;
?}
}
?
public?class?Client{
?public?static?void?main(String[]?args){
??B?b?=?new?B();
??System.out.println("100-50="?+?b.func1(100,?50));
??System.out.println("100-80="?+?b.func1(100,?80));
??System.out.println("100+20+100="?+?b.func2(100,?20));
?}
}
輸出;
100-50=150
100-80=180
100+20+100=220
可以看到,原本正常的減法運(yùn)算發(fā)生了錯(cuò)誤。原因就是類 B 在給方法起名時(shí)重寫(xiě)了父類的方法,造成所有運(yùn)行相減功能的代碼全部調(diào)用了類 B 重寫(xiě)后的方法,造成原本運(yùn)行正常的功能出現(xiàn)了錯(cuò)誤。
這樣做就違反了 LSP,使程序不夠健壯。更通用的做法是:原來(lái)的父類和子類都繼承一個(gè)更通俗的基類,原有的繼承關(guān)系去掉,采用依賴、聚合,組合等關(guān)系代替。
接口隔離原則(ISP)
Clients should not be forced to depend on methods they do not use. –Robert C. Martin
軟件設(shè)計(jì)師應(yīng)該在設(shè)計(jì)中避免不必要的依賴。
ISP 的原則是建立單一接口,不要建立龐大臃腫的接口,盡量細(xì)化接口,接口中的方法要盡量少。
也就是說(shuō),我們要為各個(gè)類建立專用的接口,而不要試圖去建立一個(gè)很龐大的接口供所有依賴它的類去調(diào)用。
在程序設(shè)計(jì)中,依賴幾個(gè)專用的接口要比依賴一個(gè)綜合的接口更靈活。
單一職責(zé)與接口隔離的區(qū)別:
單一職責(zé)原則注重的是職責(zé);而接口隔離原則注重對(duì)接口依賴的隔離。 單一職責(zé)原則主要是約束類,其次才是接口和方法,它針對(duì)的是程序中的實(shí)現(xiàn)和細(xì)節(jié);而接口隔離原則主要約束接口。
舉個(gè)例子:

首先解釋一下這個(gè)圖的意思:
「犬科」類依賴「接口 I」中的方法:「捕食」,「行走」,「奔跑」;「鳥(niǎo)類」類依賴「接口 I」中的方法「捕食」,「滑翔」,「飛翔」。
「寵物狗」類與「鴿子」類分別是對(duì)「犬科」類與「鳥(niǎo)類」類依賴的實(shí)現(xiàn)。
對(duì)于具體的類:「寵物狗」與「鴿子」來(lái)說(shuō),雖然他們都存在用不到的方法,但由于實(shí)現(xiàn)了「接口 I」,所以也 必須要實(shí)現(xiàn)這些用不到的方法,這顯然是不好的設(shè)計(jì)。
如果將這個(gè)設(shè)計(jì)修改為符合接口隔離原則的話,就必須對(duì)「接口 I」進(jìn)拆分。

在這里,我們將原有的「接口 I」拆分為三個(gè)接口,拆分之后,每個(gè)類只需實(shí)現(xiàn)自己需要的接口即可。
依賴倒置原則(DIP)
High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.– Robert C. Martin
高層策略性的代碼不應(yīng)該依賴實(shí)現(xiàn)底層細(xì)節(jié)的代碼。
這話聽(tīng)起來(lái)就讓人聽(tīng)不明白,我來(lái)翻譯一下。大概就是說(shuō)在寫(xiě)代碼的時(shí)候,應(yīng)該多使用穩(wěn)定的抽象接口,少依賴多變的具體實(shí)現(xiàn)。
舉個(gè)例子:
看下面這段代碼:
public?class?Test?{
????public?void?studyJavaCourse()?{
????????System.out.println("張三正在學(xué)習(xí)?Java?課程");
????}
????public?void?studyDesignPatternCourse()?{
????????System.out.println("張三正在學(xué)習(xí)設(shè)計(jì)模式課程");
????}
}
上層直接調(diào)用:
public?static?void?main(String[]?args)?{
????Test?test?=?new?Test();
????test.studyJavaCourse();
????test.studyDesignPatternCourse();
}
這樣寫(xiě)乍一看并沒(méi)有什么問(wèn)題,功能也實(shí)現(xiàn)的好好的,但仔細(xì)分析,卻并不簡(jiǎn)單。
第一個(gè)問(wèn)題:
如果張三又新學(xué)習(xí)了一門(mén)課程,那么就需要在 Test() 類中增加新的方法。隨著需求增多,Test() 類會(huì)變得非常龐大,不好維護(hù)。
而且,最理想的情況是,新增代碼并不會(huì)影響原有的代碼,這樣才能保證系統(tǒng)的穩(wěn)定性,降低風(fēng)險(xiǎn)。
第二個(gè)問(wèn)題:
Test() 類中方法實(shí)現(xiàn)的功能本質(zhì)上都是一樣的,但是卻定義了三個(gè)不同名字的方法。那么有沒(méi)有可能把這三個(gè)方法抽象出來(lái),如果可以的話,代碼的可讀性和可維護(hù)性都會(huì)增加。
第三個(gè)問(wèn)題:
業(yè)務(wù)層代碼直接調(diào)用了底層類的實(shí)現(xiàn)細(xì)節(jié),造成了嚴(yán)重的耦合,要改全改,牽一發(fā)而動(dòng)全身。
基于 DIP 來(lái)解決這個(gè)問(wèn)題,勢(shì)必就要把底層抽象出來(lái),避免上層直接調(diào)用底層。

抽象接口:
public?interface?ICourse?{
????void?study();
}
然后分別為 JavaCourse 和 DesignPatternCourse 編寫(xiě)一個(gè)類:
public?class?JavaCourse?implements?ICourse?{
????@Override
????public?void?study()?{
????????System.out.println("張三正在學(xué)習(xí)?Java?課程");
????}
}
public?class?DesignPatternCourse?implements?ICourse?{
????@Override
????public?void?study()?{
????????System.out.println("張三正在學(xué)習(xí)設(shè)計(jì)模式課程");
????}
}
最后修改 Test() 類:
public?class?Test?{
????public?void?study(ICourse?course)?{
????????course.study();
????}
}
現(xiàn)在,調(diào)用方式就變成了這樣:
public?static?void?main(String[]?args)?{
????Test?test?=?new?Test();
????test.study(new?JavaCourse());
????test.study(new?DesignPatternCourse());
}
通過(guò)這樣開(kāi)發(fā),上面提到的三個(gè)問(wèn)題得到了完美解決。
其實(shí),寫(xiě)代碼并不難,通過(guò)什么設(shè)計(jì)模式來(lái)設(shè)計(jì)架構(gòu)才是最難的,也是最重要的。
所以,下次有需求的時(shí)候,不要著急寫(xiě)代碼,先想清楚了再動(dòng)手也不遲。
這篇文章寫(xiě)的特別辛苦,主要是后半部分理解起來(lái)有些困難。而且有一些原則也確實(shí)沒(méi)有使用經(jīng)驗(yàn),單靠文字理解還是差點(diǎn)意思,體會(huì)不到精髓。
其實(shí),文章中的很多要求我都做不到,總結(jié)出來(lái)也相當(dāng)于是對(duì)自己的一個(gè)激勵(lì)。以后對(duì)代碼要更加敬畏,而不是為了實(shí)現(xiàn)功能草草了事。寫(xiě)出健壯,優(yōu)雅的代碼應(yīng)該是每個(gè)程序員的目標(biāo),與大家共勉。
如果覺(jué)得這篇文章還不錯(cuò)的,歡迎點(diǎn)贊和轉(zhuǎn)發(fā),感謝~
推薦閱讀:
參考資料:
《架構(gòu)整潔之道》 https://www.cyningsun.com/08-03-2019/solid-go-design-cn.html https://blog.csdn.net/yabay2208/article/details/73739514 https://zhuanlan.zhihu.com/p/92488185
