我的程式開發原則

進入程式的行業也一年多了,雖然能力不到頂尖,對於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也可以快速找到並且修復。

以上這些是在我遇到非常詭異的專案所歸納出來的心法,未來開發有碰到相關的問題也會更新在此篇文章之中。

 

如果你也有相關的心得或是建議,歡迎隨時在下方留言討論

內文如有不正確之處也請不吝指教


LINE討論群FB討論區

歡迎您的加入,讓這個社群更加美好!

聯絡方式:
FaceBook
E-Mail