Unity 遊戲架構:命名的力量--變量
變量的命名入門
大家先來試着了解一下這段代碼:
var todoList = new TodoList();
todoList.Todos = new List();
var todo = new Todo() { Id = 0, Finished = false, Content = "測試" };
todoList.Todos.Add(todo)
todo.Finished = true;
代碼本身很簡單,就算不用去看 TodoList 類和 Todo 類的定義,也是可以讀懂以上代碼的。
那這是如何做到的呢?
答案就是:進行合适的命名。
而達到以上代碼的效果的主要是變量的命名。
因為以上出現的類都是 Model 類,而 Model 則是用來描述業務是什麼,而 Model 類中大部分代碼則是變量。
比如 這是一個 TodoList App (待辦事項),其中:
有待辦事項清單
待辦事項清單有待辦事項
待辦事項可以完成和未完成。
待辦事項可以編輯文本。
以上幾點一般是我們接收到任務并了解業務之後梳理出來的。如果設計成代碼結構,結構大緻如下:
待辦事項清單
待辦事項
完成/未完成
文本
很自然 Model 的代碼就寫出來了,代碼如下。
TodoList.cs
public class TodoList
{
public List<Todo> Todos;
}
Todo.cs
public class Todo
public int Id;
public bool Finished;
public string Content;
為了讓以上類更容易地在其他檔案中了解,筆者在命名上做了哪些工作呢?
TodoList 類中的 Todos 變量:
Todos 是複數,說明其類型可能是數組、List 或者其他的集合。
Todos 首字母大寫,說明通路權限是 public 類型的。
Todos 名字本身是一個名詞,解釋成中文則是:要做的事(待辦事項),本身準确地傳達了變量的意思。
Todo 類
Id,和 Todos 一樣,傳達了通路權限,和意思(辨別),但是沒有傳達其類型。不過,不管是 int 還是 string 都無所謂。
Finished,也是一樣的,public 權限、意思(完成了)。同樣也沒有傳達其類型,不過這裡可以這樣了解,Finish 是動詞,加上 ed 則是過去式,有時态說明有狀态,這個狀态隻有兩個,完成和未完成,是以是 bool 類型,這是筆者的一個習慣,當然也可以命名為 IsFinish,等等。
Content,中文意思是内容,也就是文本内容,一般為 string 類型,因為首字母大寫,是以也是 public 權限。
簡單幾行代碼,就可以傳達這麼多資訊,是以 命名是一門藝術。
小結
在對一個變量進行命名的時候,無非要思考以下三點:
描述
權限
類型
權限和類型的表達很容易,因為關于這方面的方法論(套路)是固定的,可以很容易掌握的。
比較考驗功底的是變量的描述,是需要長期進行訓練的。
描述、權限、類型
接下來以這三點為主,分别進行方法論的介紹。
先從最簡單的來,最簡單的則是 權限 的表達
權限指的是通路權限,在 C# 中有 public、private、protected、internal 類型。
這裡在進行變量命名時分為兩種。
可外部通路的 public 和 internal。
和不可被外部通路的 private 和 protected。
權限的表達,筆者自己的習慣是,可被外部通路的變量采用首字母大寫。
比如:
public string Name;
internal static int ReferenceCount;
而不可被外部通路的變量采用 m 字首。
private string mShowedText;
protected int mMgrId;
這是筆者長期的一個習慣,也是筆者自己維護的 QFramework 的命名規範。
是以關于權限的表達非常簡單,依靠代碼規範就能搞定了,規範怎麼定就怎麼用。
這一點是最最最容易達到的。
如果各位還沒有開始在意命名這件事情,從權限開始上手是最容易的。
變量是有類型的,最好呢是把類型能夠表達出來。
類型的表達,當然這也可以依照某些代碼規範來解決的。
比如匈牙利式的代碼規範:
private int m_nAge; // n 代表數量
private string m_strName // str 代表 string
不過這種規範需要适應一段時間,這裡筆者不太推薦新手用這種規範解決類型表達問題。
而是采用一種比較簡潔、優雅的方式,來表達變量的類型。
public int Age;
Age 年齡,很容易想到是 int 類型。
Name 名字很容易想到是 string 類型。
public int PetCount;
public int PetNumber;
寵物數量,很容易想到是 int 類型。
public bool Completed;
Completed 完成了,是 bool 類型。
而這部分的詳細内容會在接下來的一個小節中詳細介紹。
關于變量的描述的内容就比較多了,但是原則很簡單,就兩個字:準确。
為變量命名時最重要的考慮事項是,該名字要完全、準确地描述出該變量所代表的事物。——《代碼大全》
如何達到準确呢?這部分也會在接下來的一個小節中介紹。
在本小節,先給大家對變量所用的詞類型進行簡單的分類:
事物
string
Name
NickName
int
Age
XXXCount
Times
…
狀态
過去式
Finished
進行時
Waiting
一些不推薦使用的(IsXXX):
IsFinished
IsFinish
IsWaiting
形容詞
active
其他
C# 中特有的 委托、事件 等。
OnXXXFinished
OnBeforeDestroy
OnLoadDone
OnLoadStart
是以在之後詳細講述變量的描述表達部分會根據這三點來進行介紹。
讀者讀到這裡,權限的表達是最容易做到的,如果沒有意識到的童鞋們,可以在工作中開始權限的表達訓練了。
權限的表達比較适合作為變量名訓練的的第一步。
關于權限部分的内容就到此完結了,下文中不再贅述。
而剩下的類型和描述用兩個大章節進行詳細介紹。
我們先搞定相對較容易的類型的表達,這樣好讓大家掌握并快速在工作中實踐。
類型的表達
關于集合(List、Array、Dictionary 等等)
List、Array 可以使用名詞的複數來搞定。
var todos = new List();
var todos = new Todo[]{};
也可以将集合類型作為字尾表達
var todoList = new List();
var todoArray = new List();
Dictionary
因為是 key-value 類型的集合,可以使用 xxx4yyy方式命名(4 = for),這是筆者個人習慣,不強制。
var todo4Id = new Dictionary();
也可以使用類型作為字尾。
var todoDict = new Dictionary();
但是 key-value 類型使用 類型作為字尾表達效果較差,是以推薦 xxx4yyy 方式。
其他的集合類型,比如 Queue、Stack 等,也同樣可以采用類型字尾的方式。
var panelStack = new Stack();
var msgQueue = new Queue();
Stack 和 Queue 是技術概念,是以一般會在架構/插件/工具層使用,在業務層使用得較少。
集合類型部分介紹到這裡。
數值/資料類型、string/char 等 (事物)
數值類型(int/flout/double):
int 類型常見的字尾詞(限定詞):
Number
var todoNumber = todos.Length;
Index
var todoIndex = todos.IndexOf(todo);
Count
var todoCount = todos.Count;
int 類型常見的字首詞:
num
var numTodos = todos.Length; // number of todos 縮寫,不太常用。
float/double 類型常見的字尾詞
Average、Sum 等。
var ageAverage = ageSum / peopleCount;
這種類型的字尾詞和字首詞在計算時經常會用到。
這裡叫做計算限定詞。
下面列出常用的計算限定詞:
Count、Number、Num、Index、Total、Sum、Average、Max、Min、Record 等。
《代碼大全》建議,除了 Num 之外其他的都建議放到字尾
還有一些數值類型的名字本身就表達了其類型。
age(int)、times(int) 等。
資料類型(string/char):
資料類型沒什麼好說的,隻能盡量選取名字本身就表達其類型的這種名字,而這部分的内容算是描述的表達部分,這裡先列出一部分:
username(string)、password(string)、name(string)、content(string)、title(string)、description(string)、id(int/string) 等。
bool、枚舉(狀态)
bool 類型:
使用動詞 + 時态。
done
finished
completed
updating
enabled
使用 Is + 名詞。
isEnemy
給 bool 類型取名的原則很簡單,隻要表示其狀态隻有兩種就好。
比如 GameObject 要麼為 激活狀态 (active 為 true) ,要麼為 未激活狀态 (active 為 false)。符合這樣的就是一個很好的 bool 變量名,不可能有第三個狀态的。
再比如,一個任務要麼完成(finished 為 true),要麼為未完成(finished 為 false)。
枚舉
枚舉部分要分兩個部分講,一個是定義部分,一個是變量部分。
枚舉的定義:
// 事件
public enum UIMainPanelEvent
// 事件監聽
OnModelDataChanged,
// 指令 (動賓短語、動詞)
UpdateView
// 狀态 (與 bool 類型一樣)
public enum ResLoadState
LoadBegan,
Loading,
LoadEnded,
// 結果 (名詞、形容詞)
public enum ResLoadResult
Success,
Fail,
定義部分和 bool 類型很像,比如 ModelDataChanged 就是 動詞 + 時态的方式。
Unity 特有的 GameObject、Transform、Button、Image 等
GameObject heroObj;
GameObject heroGameObj;
Transform heroTrans;
Button btnEnterMainPanel;
Image logoImage;
以上為常見的字首、和字尾。
關于類型的表達介紹就到這裡。
到這裡我們對類型的表達進行了詳盡的介紹。
筆者關于類型表達的方法論也都全部介紹完了,大家可以筆者的方法論為準進行練習,在此基礎上進行不斷思考改進稱為自己的方法論。
思考什麼呢?
思考如何讓别人(或自己)在别的檔案中使用變量就能猜到其類型呢?
是以筆者介紹的方法論不是重點也不是權威,重點是不斷進行思考。
描述的表達
終于到了重頭戲了。
描述的表達是能夠考驗一個開發者功底的工作。
對于 Model 類的變量命名。
要想有一個好的描述,必須非常了解業務的。
因為隻有非常了解業務了,才能找到非常準确的描述。
就像本 Chat 的開頭的入門那樣:
業務最起碼應該是梳理過一次的,梳理成自己能了解。
梳理過一次之後要再進行一次結構化的梳理,這樣比較好轉化為代碼。
結構化梳理之後才能定義 Model 類和其變量。
以上對各位的要求會比較高,是以筆者這裡建議,大家熟練掌握了了權限和類型的表達後在進行描述的練習。
而在這之前,本 Chat 也隻是介紹了 Model 的變量命名。
這是因為 Model 的變量定義是相對容易的。但是我們的工作不隻有定義 Model,還有好多其他的類需要我們定義,而定義類多少要定義一些變量。而本 Chat 不能覆寫所有的變量命名場景,是以會給出一些準則和練習步驟來給大家參考。
練習步驟一: 代碼部分全部為英文。
在 Unity 中少數的需要顯示在編輯器上提供參數調整的變量可以用一些中文漢字。而剩餘的最好是全部使用英文。
這裡最好是在編碼時随手備着一個查詞軟體,可以通過鍵盤快捷鍵快速查詢的。要養成這個習慣。到最好發現,在某一個領域或某種業務常用的詞彙就那些,自己都記住了,很少會再編碼時使用查詞軟體。
練習步驟二: 業務命名而不是技術命名
這個标題是筆者自己了解的,《代碼大全》的描述是變量名應該描述的是 what 而不是 how。也就是變量是什麼而不是怎麼做。
因為業務概念是現實的模拟,而技術概念隻是手段。是以筆者了解為為了業務命名而不是為技術命名。
要了解此準則非常簡單。摘抄《代碼大全》的一段話給大家了解:
一個好記的名字反映的通常都是問題,而不是解決方案。一個好名字通常表達的是“什麼”(what),而不是“如何”(how)。一般而言,如果一個名字反映了計算的某些方面而不是問題本身,那麼它反映的就是“how”而非“what”了。請避免選取這樣的名字,而應該在名字中反映出問題本身。
一條員工資料記錄可以稱作 inputRecord 或者 employeeData。inputRecord 是一個反映輸入、記錄這些計算概念的計算機術語。employeeData 則指的是問題領域,與計算世界無關。與此類似,對一個用于表示列印狀态的位域來說,bitFlag 就要比 printerReady 更具計算機特征。在财務軟體裡,calculateValue 的計算痕迹(how) 也要比 sum (what) 明顯。
書中原文解釋的很清楚了,不再多說了。
練習步驟三:厘清楚何時用技術命名
這裡要說一點注意的,要厘清楚在哪個層。舉一個筆者架構中的 SerializeHelper (序列化器),序列化是技術概念,是“怎麼做?” (how),但是它可以做遊戲資料的存儲等操作。那麼它為什麼不叫做 DataSaver/DataLoader 這些名字呢?
這是因為 SerializeHelper 這個類不在業務層,而是在架構層。架構本身提供的更多的是工具。
練習步驟給出了 1、2、3,很容易上手,命名這件事本身比較容易掌握,難得是初學者非常不在意。老手呢一般趟坑躺多了自然就會意識到。而初學者再去趟一下這些坑有點浪費時間,不如一開始就意識到命名這件事的重要性。
到此呢,權限、類型、描述的練習步驟和原則(準則)都給了。接下來簡單聊聊,為什麼要命名和如何更容易地落實命名這件事到工作中呢?
為什麼要命名?
代碼的本質是資訊,其承載着資訊,這個資訊可以被編譯器了解,也要被人了解。
首先你,代碼要被編譯器了解,這是最基本的要求,也代表着開發者能和計算機溝通,讓計算機幫忙處理任務。
其次呢,是要被人了解。
這些人可能是:
自己(日常)
與自己朝夕相處時間最長的就是這些代碼了,如果代碼非常容易讀懂并且整潔,每天的工作狀态都會不一樣。
在 debug 時候,還是要去讀代碼的,好的代碼是更容易被閱讀的,容易被閱讀 debug 所花費的時間就會減少。
增加功能,也還是一樣需要閱讀代碼的,以上同理。
使自己對項目的了解更深刻、清晰。
同僚(協作)
同樣代碼被容易閱讀,同僚的效率也有所提升,也會得到同僚的認可,
leader(晉升)
很多團隊有代碼 review 的文化,好的代碼更容易獲得上級和同僚的認可。
得到同僚和上級的認可,晉升就是很自然的事情了。
來接手項目的人(離職)
離職的時間也會被節省,不會被拖着問這是什麼那是什麼。
所有開發者(開源)
節省非常多人的時間,時間就是生命。
讀者(教學,正在讀本 chat 的各位則是這個範圍的)
最終受益最多的還是自己,而投入的隻不過每個變量多花費了幾秒的思考時間而已。
總之協作這個詞有一個協字,可以了解成妥協的協,也可以了解成協助的協。
給變量合适的命名既是一種妥協也是一種協助。
這就是為什麼筆者單開一個 chat 來寫一個大家認為如此簡單的事情。
如何落地變量的命名
單寫這一小節是希望大家不是隻讀一讀這篇 chat 就完事了,而是要把變量的命名這件事情落實到自己産出的代碼上去。
在工作中養成一個習慣的阻礙有很多。
最常見的原因就是項目時間緊。
這個問題很好解決,隻要訓練的時機根據實際情況進行調整就好了。
對于初學者或者項目非常緊急的開發者,在第一遍寫邏輯的時候,變量可以先随便命名(按照自己的習慣),但是隻要目前的測試點驗證通過或者功能點完成,就應該回過頭把變量的名字好好想下,進行重新命名。一般 IDE 都帶重構功能,隻要改一個地方,其他代碼部分會全部跟着更改。
這樣做的目的是為了防止同一時間做兩件事情。
邏輯編寫
變量命名
對于命名新手來說,同一時間做兩件事情會很吃力。
本身邏輯編寫就很吃力了,再加上需要思考的變量命名,會更加吃力。
是以對于命名新手要求同一時間隻思考一件事情。
對于時間非常緊急的開發者,如果進行命名的思考會影響項目的進度,那還是等項目不急的時候再落實就好了。
隻要能做到,在進行邏輯編寫時,隻考慮如何完成或者如何實作就好了。
在變量命名時,隻專注于如何取個合适的名字,隻考慮變量的描述、類型、權限就好。
如下:
變量的命名是否準确描述?
變量的命名是否表達了類型?
變量的命名是否表示了權限?
這樣堅持一段時間後,就能做到同一時間就可以做兩件事情。
初學者們隻要記住一點就夠了:
合适的命名可以不在建立變量的時候完成。
但是最終目标還是将進行合适的命名這件事情養成習慣,在建立變量的同時就已經為變量取了一個非常合适的名字。
最後,非常感謝 QFramework 的 邊上海 邊前輩對此專欄的勘誤和提供的一些非常有價值的建議。
原文位址
https://www.cnblogs.com/liangxiegame/p/12591418.html