進入程式的行業也一年多了,雖然能力不到頂尖,對於CSS還有HTML的排版還是很不熟悉。
但在開發過程中看到許多不正確的開發觀念,進而產生許多程式碼的歷史包袱,尤其是這些包袱多數來自於「資深工程師」的觀念偏差時,實在令人感到遺憾...
在某次維護的過程,真的追Code追到跑去廁所吐,於是催生了這篇文章。
這篇文章會針對經常碰到的開發觀念撰寫,不會很艱深,我也沒有那個能力寫得很艱深 > O <
算是拋磚引玉,希望大家可以多參與討論,讓糟糕的程式碼不再禍害工程師。
我想...寫出優良的程式碼,是每個工程師應盡的義務。
此篇會分為三個方向來撰寫
1.程式碼的原則:不限程式語言,對於未來維護有幫助的都會寫在這
2.Web的原則:Web中的一些基本事項
3.C#的原則:C#之中的一些小技巧
4.MVC的原則:框架帶來的好處,以及應注意事項
程式碼的原則
-
職責要清楚
電商的訂單邏輯,有可能因為物流的配合店家不同,而產生不同的判斷條件,因此要將訂單、物流的類別分別撰寫,才可以在物流的部分保留程式碼彈性。
這部分做的好,也可以使程式碼變得乾淨簡短易於維護。
新手上路的話,強烈建議上比爾叔的OOP課程,會有更深刻的理解。
-
資料要封裝
核心演算的邏輯,應該由物件自行負責,避免由外部干擾。在未來需要再針對程式碼進行拆分時,可以不用擔心外部使用到核心程式碼的部分,而不敢進行重構。
-
資料來源要統一
全域變數應由建構式及解構式處理,避免執行時呼叫全域變數時發現物件為null,以及重複new會增加GC的啟動次數。
-
資料生命週期要清楚
如果一段Method之中,變數中存放的資料不斷的被修改,應該要重新思考為何要不斷地修改資料。
會這樣說是因為曾經維護程式碼時,變數的位置找到了,改了卻發現沒效果,後來發現是資料又在後方許多地方被修改過。
當時處理這個問題時,總共被雷了三次,直到第四次受不瞭,一次從MVC的Model層追到Controller層,在追到View層,最後連JS都改過,才解決這個問題。
資料再傳遞的過層中總共被修改了五次,工程師的肝不應該這樣被傷害。
-
多用介面組合少用繼承
首先要知道,繼承並不是壞事,有著減少重複造輪子的好處,但是一個資料如果繼承超過三層,在除錯的過程中將會使你暈頭轉向的,因為很難記得程式碼的行為是在哪一層被修改的。
反而使用介面可以使程式碼架構扁平化,但缺點是實作的程式碼會倍數增加。
故繼承跟介面的使用時機需審慎評估。
Web的原則
-
狀態避免由後端控制,交由Client管理
Web本身就是無狀態的,若是狀態交由Client管理,會使架構在擴展時更容易,不用再另外處理狀態。
-
API不建議加上外框,容易造成不必要的影響
HTTP本身就有狀態碼的機制,善用這個Web的規範,而不是自行再定義一套規範
舉個例子,伺服器每次回應的訊息若是都加上ResultCode=1,此時再來取得Data,這類型訊息進行處理時有幾個缺點
1.Server回傳狀態500、404,前端將會拿不到狀態碼,而無法進行後續處理
2.新進人員必須重新閱讀這種自定義規範,對於企業而言這也是成本之一
-
Client端資料是可被竄改的、不被信任的
較為敏感的資料需由後端驗證,如訂單的金額不該由前端提供,而是應該由後端自行進DB查詢商品金額,再進行加總。
如會員帳號資訊,也應該透過Server的授權機制來判定,例如Token。
如果這樣很難懂的話請參考這個例子
URL:GetAddress?Account=Jeff,代表了我是Jeff,我可以拿到地址,萬一有心人士把Jeff改成其他用戶的帳號,用戶的隱私何在?
-
後端是最後防線務必堅守
上方已經說過了Client是不被信任的,因此後端是最後防線,所有有關安全性的東西,後端都必須實作。
我曾遇過一個問題,某些機制是由後端判斷後回傳true或是false給前端,再進行後續動作,而這個動作只是某個API而已。
交由前端處理後續動作會產生什麼問題呢?只要不打這支驗證API,立即可以繞過這個驗證機制,這樣難道不是資安問題嗎?
-
同源政策
同源政策僅存在於有實作瀏覽器的環境底下,白話文一點就是前端的request會送出,後端的API也會收到,但是回來的reponse會被瀏覽器阻止
另外即便有同源政策,GET、POST動詞依舊可以直接把要求送到Server端,要防範這種事情請善用請求的動詞
例如PUT會先發送一個request為opstion,若是伺服器接受請求,才會告知前端說,你可以發送請求,此時才會真正的發出PUT請求
-
JS撰寫時期變數無形別,執行時期變數有型別
JS在編譯的時期,宣告字串或是數值型別,後續在直接更改型別,都是被接受的
但執行時,部分情況會自動轉型,但少數情況下會產生意外情況,例如「1 + 1 = 2」以及字串的「 "1" + "1" = "11"」
-
避免在前端運算
請在瀏覽器中按下F12,然後輸入0.35+0.33,答案會等於0.6799999999999999,但這樣是對的嗎?
JavaScript還有一些較為奇怪的特性,這部分建議上保哥的JavaScript開發實戰:核心概念
另外建議可以使用TypeScript進行撰寫,將會享受到撰寫時期具有型別的好處,但記得將型別好好地宣告出來唷!
C#的原則
-
注意static變數的資源競爭
有部分人對static有點誤解,static代表在程式碼之中只存在一份,因此static的變數要盡量避免使用,否則有可能產生以下問題
A使用者使用變數時,變數值為1,結束時修改為2
B使用者在進入時,預期變數值為1,卻碰上了A使用者將變數值改成2
若是因此產生BUG不是很冤嗎?
另外,static method是不會有資源競爭的情況產生的,實際上程式碼編譯完畢後,所有class中的method都是static method XD
-
善用變數保存資料
這點是自己雷了自己很久的體悟,相信有些人會Lambda都很喜歡Select來Where去,如果是單層式的還好,若是多層式的在未來除錯時候真的會DEBUG到崩潰
建議的改善方式是程式碼執行到一定階段,就暫存一個變數,再拿這個變數繼續往下算,未來要除錯時,逐行執行也可以看到結果了
-
善用強型別特性
C#之所以我很喜歡,有幾個很簡單的原因,具有Visual Studio這個地表最強編譯器可以及時提供可使用的變數、方法以外
多數情況下nameof、GetType().Name等等的動作,都可以直接取得當前執行的資料名稱,善用這些,在未來類別、變數要重新命名時只需要右鍵>重新命名即可
仔細想想,要是程式碼之中有一百個地方取得資料都是用dr["id"]的這種寫法,要找幾個id來改?
-
善用介面保持實體可抽換
介面可以保持程式碼的實體隔離,什麼是實體隔離呢?
舉上方的物流來說,黑貓、新竹物流都會送貨,但中間轉送的機制、運費的處理、到貨的天數完全都不一樣
因此我們可以定義一個介面叫作物流,底下告知,物流有送貨、轉送、運費、到貨天數的介面,底下分別實作黑貓、新竹物流二間廠商,便可將二者清楚的切割,避免程式碼產生耦合
程式碼就會更容易維護啦
-
區分程式碼中的常數以及環境變數
程式碼的常數以及環境變數建議要好好的定義一下,例如圖片的存放路徑這種東西,不管在哪個環境資料夾名稱都會一樣,頂多存放的機器不一樣
而存放的機器就是環境變數,建議寫在config之中
而存放資料夾的路徑就是程式碼的常數,建議寫在常數之中
雖然也可以混用啦...但我個人是滿懶惰改config這種東西
MVC的原則
-
減少使用ViewBag
ViewBag是一個非常好用的東西,但也是一種毒藥,為什麼這樣說呢?
ViewBag是當前request的static dynamic的變數,這是什麼意思呢?
dynamic是一個可以任意給予資料、型別的動態型別,撰寫時期不檢查,動態時期才檢查
這二者產生放在一起,會有某Action建立一個ViewBag.ID,而在View的地方又載入另一個Action的資料,裡面也有ViewBag.ID的變數
這時候會有後蓋前的問題,進而導致資料的來源容易被重複修改,使用上應注意
-
善用ActionFilter
ActionFilter是MVC幫我們建立好的功能,用途在MVC的執行生命週期可以隨時進行額外的處理
例如進入Controller之前、進入Action之前、Action執行完畢之後,MVC會在這些時間點發生時,自動去爬有沒有相關的Attribute可以使用,有的話則執行
因此例如登入、權限、Log、錯誤等東西,都可以透過ActionFilter進行處理,會使程式碼的簡潔度大幅提升
-
善用模型驗證
前面有說了後端是最後防護的手段,既然如此模型驗證資料是否存在就是一個基本應該做的功能,可以驗證資料是否合乎預期,避免要送進DB或是與他人介接的資料殘破不堪,進而產生衍伸的問題
-
V跟M的關注點分離
Model是跟外部溝通時,傳遞、接收的工具
ViewModel是UI跟程式碼介接的工具
那這二者差在哪呢?
Model只負責取資料,但是不做邏輯演算,而ViewModel,是Model取完資料的介接層,亦即透過Model轉換成ViewModel時,UI該如何呈現資料,需要透過什麼的邏輯運算來呈現,都該在這層處理完畢。
透過這樣可以在View層,得到最乾淨的HTML來顯示,那這樣會帶來什麼樣的好處呢?
在調整UI跟調整程式碼的職責清楚的分割了,若是要加一個HTML時,可以清楚的了解HTML在哪裡,而不是看到許多if else的多層嵌套在View的部分
這會讓後續維護的人員在調整HTML時候,可以避免不知道該調整一處、二處還是多處,既然如此是不是可以早一點下班呢?
透過以上這些自我約束,程式碼變得較為清晰乾淨,自然而然BUG就少了,即使出了BUG也可以快速找到並且修復。
以上這些是在我遇到非常詭異的專案所歸納出來的心法,未來開發有碰到相關的問題也會更新在此篇文章之中。
如果你也有相關的心得或是建議,歡迎隨時在下方留言討論
內文如有不正確之處也請不吝指教
歡迎您的加入,讓這個社群更加美好!