
There are only two hard things in Computer Science: cache invalidation and naming things.-- Phil Karlton
軟體開發中一個著名的反直覺就是“起名兒”,這個看上去很平凡的任務實際上很有難度,身邊統計學顯示,越是有經驗的程式員,越為起名頭痛,給小孩起名兒都沒這么費勁,
命名的困難可能來自于以下幾個方面:
-
資訊壓縮:命名的本質是把類/方法的資訊提煉成一個或幾個詞匯,這本身需要對抽象模型的準確理解和概括, -
預測未來:類/方法的職責可能會在未來有變化,現在起的名字需要考慮未來可能的變動, -
語言能力:缺少正確的語法知識,或是缺少足夠的詞匯量,本來英文就不是大部分中國人的母語,更甚者,計算機的詞匯表不同于日常交流詞匯表,有大量黑話, -
不良設計:混亂的職責分布、不清晰的抽象分層、錯誤的實作,都會導致無法起出好的名字,在這個意義上,起名字其實是對設計的測驗: 如果起不出名字來,很可能是設計沒做好 -- 重新想想設計吧,
命名就像寫作,會寫字不等于會寫作,而且,命名更多像是一門藝術[注](此處藝術的含義取自于 Knuth -- 命名會訴諸品味和個人判斷,),不存在一個可復制的命名操作手冊,
本文描述一些實用主義的、可操作的、基于經驗的命名指南,并提供了一個代碼詞匯表,和部分近義詞辨析,本文沒有涉及討論名字的形而上學,例如如何做更好的設計和抽象以利于命名,也沒有涉及如何劃分物件等,也無意討論分析哲學,
命名原則
命名是一門平衡準確性和簡潔性的藝術 -- 名字應該包含足夠的資訊能夠表達完整的含義,又應該不包含冗余的資訊,
準確 Precision
名字最重要的屬性是準確,名字應該告訴用戶這個物件/方法的意圖 -- “它是什么” 和 “它能做什么”, 事實上,它是體現意圖的第一媒介 -- 名字無法表現含義時讀者才會閱讀檔案,
名字應該是有資訊量的、無歧義的,以下一些策略可以增加名字的準確度:
可讀
最基本的語法原理,是一個類(Class/Record/Struct/... 隨你喜歡)應該是一個名詞,作為主語,一個方法應該是動詞,作為謂語, 換言之,類“是什么”,方法“做什么”, 它們應該是可讀的,應該是 [Object] [Does ...] 式的句子,
可讀是字面意思,意味著它應該是通順的,所以應該:
避免 API 中使用縮寫
就像是給老板的匯報中不會把商業計劃寫成 Busi Plan 一樣,也不應該在公開 API 中使用一些奇怪的縮寫,現在已經不是 1970 年了,沒有一個方法不能超過 8 個字符的限制,把類/方法的名字寫全,對讀者好一點,可以降低自己被同事打一頓的風險,
creat 是個錯誤,是個錯誤,是個錯誤!
但是,首字母縮略詞的術語是可行并且推薦的,如 Http, Id, Url,
以下是可用的、得到普遍認可的縮寫:
-
configuration -> config -
identifier -> id -
specification -> spec -
statistics -> stats -
database -> db (only common in Go) -
regular expression -> re/regex/regexp
未得到普遍認可的縮寫:
-
request -> req -
response -> resp/rsp -
service -> svr -
object -> obj -
metadata -> meta -
business -> busi
req/resp/svr 在服務名稱中很常見,這非常糟糕,請使用全稱,
再次說明:以上的說明是針對 API 名稱,具體包括公開物件/函式名字、RPC/Web API 名字,在區域變數使用縮寫不受此影響,
避免雙關
對類/方法的命名,不要使用 2 表示 To, 4 表示 For,
func foo2Bar(f *Foo) *Bar // BAD
func fooToBar(f *Foo) *Bar // GOOD
func to(f *Foo) *Bar // Good if not ambiguous.
2/4 這種一般只有在大小寫不敏感的場合才會使用,例如包名 e2e 比 endtoend 更可讀,能區分大小寫的場合,不要使用 2/4,
合乎語法
雖然不能完全符合語法(例如通常會省略冠詞),但是,方法的命名應該盡量符合語法,例如:
class Car {
void tireReplace(Tire tire); // BAD, reads like "Car's tire replaces"
void replaceTire(Tire tire); // GOOD, reads like "replace car's tire"
}
關于命名的語法見“語法規則”一章,
使用單一的概念命名
命名本質上是分類(taxonomy),即,選擇一個單一的分類,能夠包含類的全部資訊,作為名字,
考慮以下的角度:
例如,把大象裝進冰箱,需要有三步 -- 打冰箱門打開,把大象放進去,把冰箱門關上,但是,這可以用單一的概念來描述:“放置”,
class Fridge {
public void openDoorAndMoveObjectIntoFridgeAndCloseDoor(Elephant elphant); // BAD
public void put(Elephant elphant); // GOOD
}
應該使用所允許的最細粒度的分類
避免使用過于寬泛的類別,例如,這世界上所有的物件都是“物件”,但顯然,應該使用能夠完整描述物件的、最細顆粒度的類別,
class Fridge {
public put(Elephant elephant); // GOOD.
public dealWith(Elephant elephant); // BAD: deal with? Anything can be dealt with. How?
}
簡而言之,名字應該是包含所有概念的分類的下確界,
簡潔 Simplicity
名字長通常會包含更多資訊,可以更準確地表意,但是,過長的名字會影響可讀性,例如,“王浩然”是一個比“浩然·達拉崩吧斑得貝迪卜多比魯翁·米婭莫拉蘇娜丹尼謝莉紅·迪菲特(defeat)·昆圖庫塔卡提考特蘇瓦西拉松·蒙達魯克硫斯伯古比奇巴勒·王”可能更好的名字,(來自于達啦崩吧)
在此,我提出一個可能會有爭議的觀點:所有的編程語言的命名風格應該是趨同的,不同于通常認為 Java 的命名會傾向于詳盡,Go 的命名會傾向于精簡,所有的語言對具體的“名字到底有多長”的建議應該是幾乎一樣的 -- 對外可見應該更詳細,內部成員應該更精簡,具體地:
-
public,如 public 類的名字、public 方法的名字 - 應該詳細、不使用縮寫、減少依賴背景關系,通常是完整名詞短語, -
non-public,如類成員、私有方法 - 不使用縮寫、可以省略背景關系,下界是單詞,不應該使用單字符, -
local,如函式的區域變數 - 基本上是風格是自由的,不影響可讀性的前提下,例如函式方法長度很短,可以使用單字符指代成員,
上述規則像是 Go 的風格指南,但是,并沒有規定 Java 不能這樣做,事實上,Java 的冗長是 Java 程式員的自我束縛,即使在 Java 的代碼里,也可以這樣寫:
public class BazelRuntime {
public boolean exec(Command cmd) {
String m = cmd.mainCommand(); // YES, you can use single-letter variables in Java.
// ...
}
}
同樣,在 Go 的代碼中也不應該出現大量的無意義的縮寫,尤其是匯出的結構體和方法,
type struct Runtime {} // package name is bazel, so bazel prefix is unnecessary
type struct Rtm {} // BAD. DO NOT INVENT ABBREVIATION!
當然,由于語言特性,在命名風格上可能會有差異,例如,由于 Go 的匯入結構體都需要加包前綴,所以結構名中通常不會重復包前綴;但 C++/Java 通常不會依賴包名,但是,上述的原則仍然是成立的 -- 可見度越高,應該越少依賴背景關系,并且命名越詳盡,
Google Go Style Guide 是唯一詳盡討論命名長度的風格指南,非常值得參考,并且不限于 Go 編程:
https://google.github.io/styleguide/go/decisions#variable-names
一致 Consistency
另一個容易被忽略的命名的黃金原則是一致性,換言之,名字的選取,在專案中應該保持一致,遵守代碼規范,避免這方面的主觀能動性,方便別人閱讀代碼,通常情況下,一個差的、但是達成共識的代碼規范,也會遠好于幾個好的、但是被未達成共識的規范,

這個圖我能用到下輩子: xkcd 927
但是僅符合代碼規范是不夠的,如同所有的語言,同一個概念,有多個正確的寫法,
考慮以下的例子:
message Record {
int32 start_time_millis = 1; // OK
int32 commited_at = 2; // Wait. Why not commit_time? Anything special?
int32 update_time = 3; // What unit? Also millis?
google.types.Timestamp end_time = 4; // WTF? Why only end_time is typed?
}
幾種都是合理的(雖然不帶單位值得商榷),但是,如果在一個代碼中出現了多種風格,使用起來很難預測,您也不想使用這樣的 API 吧?
所以,在修改代碼的時候,應該查看背景關系,選擇已有的處理方案,一致性大于其它要求,即使舊有的方案不是最好的,在做區域修改時,也應該保持一致,
另一個可考慮的建議是專案的技術負責人應該為專案準備專案的專有詞匯表,
語法規則
類/型別
類
類應該是名詞形式,通常由單個名詞或名詞短語組成,其中,主要名詞會作為名詞短語的末尾,例如 Thread, PriorityQueue, MergeRequestRepository,
-
名詞短語通常不使用所有格,如,并非 ServiceOfBook,也不是BooksService(省略 '),而是BookService,
介面
介面的命名規則和類相同,除此之外,當介面表示可行動型別時,可使用另一個語法,即 Verb-able,例如:
public interface Serializable {
byte[] serialize();
}
public interface Copyable<T> {
T copy();
}
public interface Closable {
void close();
}
(Go 通常不使用這個命令風格,只在 Java/C++ 中使用,)
輔助類
只在 Java(注 1)中使用,一個類或概念所有的輔助方法應該聚合在同一個輔助類,這個類應該以被輔助類的復數形式出現,不推薦使用 Helper/Utils 后綴表示輔助類,尤其不推薦使用 Utils/Helpers 做類名,把所有的輔助方法包進去,如:
class Collections {} // For Collection
class Strings {} // For String
class BaseRuleClasses {} // For BaseRuleClass
class StringUtils {} // WORSE!
class StringHelper {} // WORSE!
注 1: 客觀來說,這適用于所有強制 OOP 的語言(所有強制把方法放在類里的語言),但是除了 Java, 沒有別的語言這么煩啦,
方法
方法通常是謂語(動詞),或是 謂賓(動詞+名詞) 結構,注意以上語法中,動詞都在最前端,例如:
class Expander {
String expand(String attribute); // 主-謂
String expandAndTokenizeList(String attribute, List<String> values); // 主-謂-賓
}
除此之外,有以下特例值得注意:
訪問器 Getter
直接使用所 Get 的物件的名詞形式,即 Foo(),不要使用 GetFoo(),
Java: 所有的 Getter 都需要一個 get 前綴是來自于過時的 Java Beans Specification,以及 Javaer 的思想鋼印,
func Counts() int; // GOOD
func GetCounts() int; // BAD: UNNECESSARY.
斷言 Predicate
斷言函式指回傳結果是布爾型(即真偽值)的函式,它們通常有以下命名格式:
系動詞: 主-系-表
即 isAdjective() 或 areAdjective() 格式,表示是否具有某個二元屬性,類似于 Getter,可以省略系語,只使用表語,即: adjective(),
func IsDone() bool {} // OK-ish. But could be better.
func Done() bool {} // GOOD. Why bother with is/are?
func CheckEnabled() bool { // BAD. Nobody cares if it is "checked". Just tell the user if it is enabled.
return enabled;
}
func Enabled() bool {} // GOOD.
情態動詞: 主-助謂-謂-(賓/表)
情態動詞也是常見的斷言形式,常見的是以下三個:
-
should: 查詢當前是否應該執行給定的實義動詞, -
can: 查詢當前類所在狀態是否可以執行給定的實義動詞,某些情況下,也可以使用第三人稱單數作為更簡潔的代替, -
must: 特殊形式,不同于前兩者,會執行給定的實義動詞,must 表示執行必須成功,否則會拋出不可恢復錯誤 (throw/panic),類似于 C++ 中常見的 OrDie 后綴,
func Compile(s string) Regexp, error // Returns error upon failure
func MustCompile(s string) Regexp // Panics upon failure
func (r Regexp) CanExpand(s string) bool // Whether s is legal and can be expanded
func (r Regexp) Expands(s string) bool // Whether r expands s, i.e. r can expand s.
func (r Regexp) ShouldReset() bool // Whether the state requires reset. Does not perform de-facto reset.
func (r Regexp) Reset() // De-facto reset.
表嘗試: 主-maybe/try-謂-(賓/表)
上文 "must" 的反面,表示嘗試性的執行,并且失敗不會造成嚴重后果:
-
maybe 前綴用以表示指定的行為有前置條件,也在方法中執行,如果前置條件不滿足,不會執行指定行為,通常不會出現在公開 API, -
try 通常用于 Try-Parse Pattern,用于避免拋出例外,
void maybeExecute() {
if (!predicate()) {
return;
}
// execute
}
std::unique_ptr<DateTime> ParseOrDie(std::string_view dateTime);
bool TryParse(string_view dateTime, DateTime* dateTime);
第三人稱單數
另一個常見場景是我們希望表示類擁有某些屬性,但是使用助動詞并不合適,如果前文描述,常見的選擇是使用第三人稱單數的靜態動詞(Stative verb)(注 1) 表示類滿足給定斷言,
func (l *List) Contains(e interface{}) bool
func (r Regexp) Expands(s string) bool
注 1: 簡單地說,靜態動詞是表示狀態的動詞,與動態動詞(Dynamic verb)表示動作對應,或言“持續性動詞”,
一階邏輯 First-order logic, Predicate Logic
一階邏輯量詞也是常見的前綴:
-
all 表示所有物件滿足給定要求 -
any 表示任意物件滿足給定要求 -
none 表示沒有任何物件滿足給定要求
語法: <一階量詞><動詞|形容詞>
class Stream {
// Returns whether all elements of this stream match the provided predicate.
boolean allMatch(Predicate<? super T> p);
// Returns whether any elements of this stream match the provided predicate.
boolean anyMatch(Predicate<? super T> p);
// Returns whether no elements of this stream match the provided predicate.
boolean noneMatch(Predicate<? super T> predicate)
}
介詞
介詞經常與某些動詞固定搭配,因此,通常可以省略動詞,而只使用介詞作為方法名稱,
-
to: 轉換至另一物件,等價于 convertTo,to 會產生一個全新的物件,通常不持有對原物件的參考, -
as: 回傳某個視圖,等價于 returnViewAs,一個“視圖(View)” 通常是對原有物件的另一角度的抽象,通常會持有對原有資料的參考,而不會產生新的資料, -
of/from/with:構造新物件,等價于 createOutOf/createFrom/createWith,見下文“工廠模式”, -
on: 監聽事件,等價于 actUpon,見下文“事件”,
class Foo {
public List<T> toList(); // Convert to (Construct a new instance of) a new List. Creates a new list.
public List<T> asList(); // Return a List as a different **view**. Holds reference of the original reference.
static Foo of(); // Construct Foo as a factory method.
static Foo from(Bar); // Construct Foo from Bar.
Foo with(Bar); // Construct a new Foo by replacing Bar with new Bar.
void onClick(ClickEvent e); // Act upon click event.
}
參考資料:
-
https://testing.googleblog.com/2017/10/code-health-identifiernamingpostforworl.html -
https://journal.stuffwithstuff.com/2016/06/16/long-names-are-long/ -
https://journal.stuffwithstuff.com/2009/06/05/naming-things-in-code/ -
https://habr.com/en/post/567870/#names_in_engineering -
https://medium.com/wix-engineering/naming-convention-8-basic-rules-for-any-piece-of-code-c4c5f65b0c09 -
https://github.com/kettanaito/naming-cheatsheet -
[[Effective Java]] Item 68: Adhere to generally accepted naming conventions
詞匯表
下文按用途歸類了常見動詞和名詞,并對同義近義詞進行了辨析,
類/名詞
類繼承
Abstract/Base Impl Default
Java
-
Interface: 通常不需要額外的表示,不要加 I前綴,或后綴FooInterface, -
Abstract class: 通常會添加 Abstract/Base前綴以明確屬性,這是因為 Interface/Impl 是常見的,Class 也是常見的,但是基于繼承的抽象類是特殊的、應該予以避免的,應該給予特殊標記, -
Implementation: -
如果不實作介面,通常不需要任何特殊修飾符, -
如果以 "is-a" 的方式實作了某個介面,那么通常實作會以 {InterfaceName}Impl的方式命名, -
如果一個類實作了多個介面,那么通常這個類應該是以作為主要目標的介面為基礎命名,例如 class BazelBuilderImpl implements BazelBuilder, AutoClosable, Serializable, -
如果一個介面有多個實作,通常會基于它們本身的實作特點命名,并且不使用 Impl后綴,Default通常用來命名默認的實作,即其它實作如果不存在會 fallback 到的實作,如果所有的實作都是平等地位,那么不要使用Default命名,
// https://github.com/bazelbuild/bazel with some fake examples
public interface SkyFunction {}
public abstract class AbstractFileChainUniquenessFunction implements SkyFunction {}
public class DefaultSkyFunction implements SkyFunction {}
public class BazelModuleInspectorFunction implements SkyFunction {}
public interface VisibilityProvider {}
public final class VisibilityProviderImpl {}
C++
-
C++ 的 interface 是通過抽象類不存在基類成員變數模擬,通常介面所有的成員函式都是公開純虛函式, -
使用 Impl表示實作, -
Abstract class: 通常會添加 Base后綴以明確屬性,這是因為 Interface/Impl 是常見的,Class 也是常見的,但是基于繼承的抽象類是特殊的、應該予以避免的,應該給予特殊標記,
// levelDB
// includes/db.h
class DB {
public:
virtual ~DB(); // MUST!
virtual Status Delete(const WriteOptions&, const Slice&) = 0;
}
// db/db_impl.h
class DBImpl : public DB {}
// rocksDB
// Base class
class CacheShardBase {}
Go
-
Go 的 interface 從來不是用來做 "is-a" 定義的,Go 的 interface 契約通過 duck typing 滿足,interface 應該在消費方定義,而非提供方,因此, interface Foo/struct FooImpl不應該出現, -
Go 也并沒有抽象類,雖然可以將一個結構體嵌入到另一個結構體中,所以 Base/Abstract也極少出現, -
原則上,Go 的類關系更為簡化,命名更強調意義優先,因此在命名時避免使用修飾性前后綴,
例外
Exception/Error
Java
所有的例外擴展應該以 Exception 為后綴,所有的錯誤應該以 Error 為后綴, 對例外和錯誤的區別請參見 https://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html
public class IllegalArgumentException;
public class OutOfMemoryError;
C++
C++ 的 exception 通常指語法特性,與 throw 對應,而 error 可以用來表示具體的例外錯誤,
// stdlib
std::exception;
std::runtime_error
Go
所有的錯誤都是 error,因此,所有自定義的對 error 的擴展都以 Error 作為后綴,
os.PathError
測驗
Test
Java/Go/C++ 均使用 Test 作為測驗類的后綴,
模塊
Module/Component
Module/Component 通常會在框架中使用,不同的語言/框架對于 Module/Component 有不同的定義, 在非框架代碼中應該減少使用 Module/Componenet 等命名,因為可能與已有框架沖突,并且 Module/Componenet 過于寬泛而缺少實質意義,
Module/Component 是意義相近的詞,都可以表示“模塊”或者“組件”,兩者雖然有細微的分別,但是框架通常都顯式(即在檔案中指定,或者通過框架語意約束)地把它們定義為框架語境下的某些結構層級,
總結,Module/Component 命名應該注意:
-
只應該在框架代碼中使用, -
Module/Component 應該在框架的語境中給出確切的定義,
服務
Service
Service 通常用于作為 C-S 架構下的服務提供者的名稱的后綴,如:
HelloService
但除此之外,Service 可以表示任何長期存活的、提供功能的組件,例如:
BackgroundService // Android 后臺服務
ExecutorService // 執行緒池執行服務,也是服務
BAD: 不要使用 Svr 縮寫,使用全稱,
容器
Holder/Container/Wrapper
Holder/Container/Wrapper 都表示“容器”,具有同一個意圖:為一個類增加額外的資料/功能,例如:
-
添加某些語境下的元資料(Decorator 模式) -
做適配以在另一個環境中使用(Adapter 模式)
通常的結構如下:
class ObjectHolder {
private final Object object;
// other stuff ...
public Object object() {}
// Other methods
}
這三個詞沒有區別,在同一個專案中,應該保持一致,
控制類
Manager/Controller
Manager 和 Controller 是同義詞,它們通常用來表示專門控制某些類的類,
這兩個詞有以下幾個常見場景:
-
Manager 管理資源,如 DownloadManager,PackageManager, -
Manager 通常特指管理物件的生命周期,從創建到銷毀, -
Controller 通常在某些架構中,尤其是 MVC (Model-View-Controller),
即使如此,Manager/Controller 是無意義詞匯,出現時充滿了可疑的味道 -- 類應該管理它們自己, Controller/Manager 多了一層抽象,而這很可能是多余的, 認真考慮是否需要 Manager/Controller,
輔助類
Util/Utility/Utils/Helper/{ClassName}s
輔助類是強制 OOP 的語言(i.e. Java) 所需要的特殊類,通常它們是一些輔助方法的合集,
Java
將與某個型別相關的輔助方法放在一個類中,并且以復數形式命名輔助類,如:
// Java std lib
public final class Strings {}
public final class Lists {}
public final class Collections {}
避免使用 Util/Utility/Utils/Helper,它們是無意義詞匯,
C++
使用全域方法,如果擔心命名污染,將之置入更細粒度的 namespace,
Go
使用全域方法,
函式式
Function/Predicate/Callback
Function 通常表示任意函式, Predicate 表示命題,即通常回傳型別為 bool, Callback 指回呼函式,指將函式作為引數傳遞到其它代碼的某段代碼的參考,換言之, Function 可以作為 Callback 使用,因此,Callback 在現代函式式編程概念流行后,通常很少使用,
Java
熟悉 Java 8 開始提供的函式式語意,如果可以使用標準庫的語意,不要自己創建名詞, 注意 Function 指單入參、單出參的函式,如果是多入參的函式,直接定義 FunctionalInterface 并且按用途命名,例如 OnClickListener.listen(Context, ClickEvent),
// java.util.function
Predicate<T> // f(T) -> bool
Function<T, R> // f(T) -> R
Consumer<T> // f(T) -> void
Supplier<T> // f() -> T
C++
first-class 函式的標準型別為 std::function,
C++ 表示命名函式物件的慣用法是 fun,Stdlib 中會縮寫 function 為 fun,如 pmem_fun_ref,因此與 stdlib 一致,在代碼中不要使用 fn 或是 func ,
Go
Go 通常使用 Func 或是 Fn 表示函式型別,
type ProviderFunc func(config ConfigSource, source PassPhraseSource) (Provider, error)
type cancelFn func(context.Context) error
在同一個專案中,應該保持一致,
作為引數時,函式不會特意標明 Fn,而是遵從普通的引數命名方式:
func Sort(less func(a, b string) int)
換言之,函式是一等公民,
設計模式類
類/方法通常都按它們的行為模式來命名,恰好,設計模式就歸類抽象了很多行為模式,所以設計模式提供了很多好的名字,
創建式
Factory: 工廠模式,通常,使用工廠方法即可,不需要一個額外的工廠類,只有當工廠特別復雜,或者工廠有狀態時再考慮使用工廠類,
Builder:構建者模式,一般來說 Builder 都是作為 inner class,如
class Foo {
static class FooBuilder {}
}
行為式
Adapter: 配接器
在 GoF 中 Adapter 本來是將一個類封裝以可以被作為另一個型別被呼叫,這樣呼叫方不需要額外改變代碼,這種用途通常被內化到容器上,見上文[容器類]部分,
在現代,Adapter 更多地被作為 資料類 -> 資料類的轉化,如常見的 pb -> pb:
class ProtoAdapter<S, T extends Message> {}
Decorator:裝飾器
在 GoF 中 Decorator 本來是將一個類作為抽象類,通過組合+繼承實作添加功能,實際上現代的編程實踐中往往通過直接提供一個容器的封裝提供裝飾功能,見上文 [容器類]部分, 所以 GoF 式 Decorator 并不常見,除非像 Python 在語法層面提供了裝飾器,在 Java 中類似的功能是注解,
Delegation:委派模式
GoF 中是非常基本的模式:由一個類負責接受請求,并把請求轉發到合適的實體類中執行,
class RealPrinter {}
class Printer {
RealPrinter printer;
}
Delegate 非常常見,也提供了兩個名字,請注意區分:
-
Delegate是被委任的物件, -
Delegator是委任物件, 所以,通常情況下Delegator在命名中會更常見,類似于Dispatcher,Delegate更多作為一個型別或是介面被實作,具體的選擇參見 [編排] 部分,
Facade: 外觀模式
GoF 中 Facade Pattern 通常是指為子系統提供一個更高層的統一界面,屏蔽子系統的獨有的細節, 在現實中,Facade 通常用來為非常復雜的類/系統定義一個較為簡化的界面,如:
// proto, extremely complicated TripResponse
message TripResponse {
// ...
// ...
string last_message = 3279;
}
class TripResponseFacade {
private final TripResponse response;
Trip trip();
Endpoint source(); // Abstracted and processed
Endpoint target(); // Abstracted and processed
}
Facade 與 Adapter 的主要區別在于 Facade 的主要目的是為了簡化,或者說更高層次的抽象,并且通常簡化的界面不服務于專門的對接類, Adapter 通常是為了一個特定的對接類實作,
注意 Facade 命名通常可以省略,僅當你的意圖是明確告知用戶這是關于某個類的外觀時使用,
Proxy:代理模式
GoF 中代理模式用來添加一層抽象,以對實際類進行控制,并添加某些行為(如 lazy/memoized),或是隱藏某些資訊(例如可見性或是執行遠程呼叫),
Proxy 與 Facade 的區別在于 Proxy 通常是為了額外的控制/記錄等行為,而非只是為了更高的抽象/簡化,
注意 Proxy 作為代碼模式時,通常不應該出現在命名之中,使用具體的 Proxy 的目的作為命名,如 LazyCar 或是 TracedRuntime,而非 CarProxy 或是 RuntimeProxy,
Proxy 還有另一個含義就是真正的“代理”,如代理服務器,在這種情況下,使用 Proxy 是合適且應該的,這也是另一個為什么代理模式不應該用 Proxy 命名的原因,
Iterator: 迭代器
時至今日仍然最常見的模式之一,Interator 有以下兩個術語,不要混淆:
-
Iterable: 迭代容器 -
Iterator: 迭代器
Visitor: 訪問者模式
訪問者模式用來遍歷一個結構內的多個物件,物件提供 accept(Visitor) 方法,呼叫 Visitor.visit 方法,
即使如此,Visitor 應該并不常見,因為它可以簡單地被函式式的寫法替換:
class Car {
void accept(Consumer<Car> visitor); // No longer need to define Visitor class.
}
Observer/Observable: 觀察者模式
Observer/Publisher/Subscriber/Producer/Consumer
時至今日最常見的模式之一,和事件驅動編程(Event-based)有緊密關系 -- Oberservable 發布訊息,所有注冊的 Obeserver 會接收訊息, Publisher/Subscriber 也是類似的,它們的區別在于 Observer 模式往往是強系結的 -- 注冊和分發通常在 Observable 類中實作; 而 PubSub 模式通常有專門的 Message Broker,即 Publisher 與 Subscriber 是完全解耦的,
PubSub 與 Producer/Consumer 的區別是:
-
Publisher/Subscriber: 在事件流系統中,表示 1:N 廣播/訂閱, -
Producer/Consumer: 在整個流系統中,專指 1:1 生產/消費,Producer/Consumer 也是 Pub/Sub 系統的組件(廣播也是一對一廣播的), -
有些系統(Kafka)使用 Consumer Group 表示 Subscriber,
所有的訊息注冊的模式由三部分組成:
-
Notification: 訊息本身 -
Sender:訊息發送者/注冊 -
Receiver: 訊息接收者
關于命名參見 [事件] 部分,
Strategy:策略模式
Strategy/Policy
策略模式在 GoF 中用以指定某個行為在不同場景下的不同實作,作為“策略”,
Strategy 模式往往不是很顯式,現代通常使用 Strategy 表示實際的“策略”,即對資訊不同的處理策略,而不采取 Strategy 模式的含義,
在“策略”這個語意中,Strategy/Policy 沒有區別,在同一個專案中,應該保持一致,
Command:命令模式
命令模式在 GoF 中以類開代表實際行動,將行動封裝,以支持重復、取消等操作,
Command 在現代編程實踐中可以通過簡單的函式式方案替換,如:
Function<T, T> command; // Java
std::function<const T&(const T&)> command; // C++
type Command func(T*) T* // Go
現代通常使用 Command 表示實際的“命令”,而不采取 Command 模式的含義,
Null Object 模式
Tombstone
Null Object 模式不在 GoF 當中,它是一個用來代替 null 的 object,對其所有的操作都會被吞掉, Null Object 主要是為了避免空指標, 合理的零值,例如 go time.Time = 0,也可以理解為一種 Null Object,
通常會有一個專門的物件表示 Null Object,可以借用 Tombstone 表示 Null Object,
Object Pool 物件池模式
Pool
物件池模式不在 GoF 當中,它是將一系列昂貴的物件創建好放在一個池子中,并使用戶通過向池子申請物件,而不再自己手動地創建/銷毀物件,最著名的池化的例子是執行緒池,即 ThreadPool,
Pool 通常用來表示物件池子,例如 ThreadPool, ConnectionPool,
Arena
Arena 是指 Region-based memory management,是指一片連續的記憶體空間,用戶在其中分配創建物件,管理記憶體,
前/后綴
并發/異步
Concurrent Synchronized Async
有時候我們需要特別標明一個類是執行緒安全的,通常這是特意為了與另一個執行緒不安全的實作做區分,典型的例子是 HashMap 和 ConcurrentHashMap,如果一個類只是單純是執行緒安全的,那么通常不需要在名字里特意說明,在檔案里說明即可,
例如:
/** This class is designed to be thread safe. */
class SomeClassThreadSafe {}
/** This class is immutable thus thread safe. */
class SomeClassImmutable {}
Concurrent 通常是用來說明該類是執行緒安全的前綴,Synchronized 是另一個在 Java 中可用的標明類是執行緒安全的前綴,但是,這通常說明這個類是通過 synchronized 機制來保證執行緒安全的,所以只在 Java 中使用,
另一個常見的場景是同一個方法有兩種實作:同步阻塞和異步不阻塞的,在這種情況下,通常會命名例外不阻塞的方法為 {synchronizedMethod}Async,例如:
public T exec();
public Future<T> execAsync();
如果一個異步的方法并沒有對應的同步方法,通常不需要加 Async 后綴,
在 Go 中,如果一個方法是意圖在其它協程中異步執行,不需要加 Async 后綴,
快取/惰性
Cached/Buffered Lazy Memoized
名詞辨析:
-
Cached 表示獲取的物件會被快取,保留一段時間,在快取期間不會重新獲取, -
Buffered 與 Cached 同義, -
Lazy 表示這個物件會被在第一次呼叫時創建,之后一直保留 -
Memoized 通常表示執行結果會在第一次計算后被記憶,之后不會再重復計算
注意 Buffered 不應該與 Buffer 混淆, Buffer 作為名詞專指“緩沖區”, 注意 Cached 不應該與 Cache 混淆, Cache 作為名詞專指“快取”,
Cached/Buffered 應該在專案中是一致的, Cached/Lazy/Memoized 取決于物件是被獲取的,還是創建的,還是計算獲得的,
不可變性
Mutable Immutable
Mutable 顯式地宣告一個類是可變的,Immutable 顯式地宣告一個類是不可變的, 通常情況下,類似于并發安全性,是否可變應該在類檔案中說明,而不應該在類名中,顯得臃腫,只有當一個類同時有可變/不可變版本時,可以使用 Class/ImmutableClass,
存盤/資料/處理
資料類
Object Data Value Record Entity Instance
上面幾個都可以用來表示一個表示資料的類,但是這些詞是典型的“無意義詞匯”,如果把它們從名字中洗掉,仍然可以表示完整意義,那么應該刪掉,
class CarObject {} // Bad
class CarEntity {} // Bad
class CarInstance {} // Bad
class Car {} // Good
class MapKey {}
class MapValue {} // OK. Couldn't be shortened.
class LoggingMetricsData {} // Bad
class LoggingMetricsValue {} // Bad
class LoggingMetricsRecord {} // Bad
class Logging Metrics {} // Good
class DrivingRecord {} // OK. Couldn't be shortened.
Statistics/Stats
表示“統計資料”, Stats 是公認的可用的 Statistics 的縮寫,Java/C++/Go 均可,
存盤
Storage Database Store DB
Cache
Verbs: - save/store/put
Storage/Database/Store/DB 都可以作為“存盤服務”,即廣義上的“資料庫”(不是必須是完整的 DBMS), 其中,在 C++/Go 中 DB 是常見且可接受的,在 Java 中通常使用全稱,
專案內應該選擇一個術語保持一致,
save/store/put 在資料庫類中是同義詞,同一個專案中應該保持一致,
資料格式
Schema Index Format Pattern
名詞辨析:
-
Schema 借用資料庫的概念,指資料的結構模式, -
Index 借用資料庫的概念,專指資料的索引, -
Format/Pattern 通常是泛指的“模式/格式”概念,實際出現時,Format/Pattern 往往和字串相關,如 Java 使用 Pattern 表示正則運算式,在非公共代碼中,Format/Pattern 通常過于寬泛,應該考慮選用更細化的名詞,
哈希
Hash/Digest/Fingerprint/Checksum
Hash/Digest 哈希是一種將任何資料映射到一個較小的空間的方法,映射通常被稱為**哈希函式(Hash Function),映射值通常被稱為摘要(Digest)**,
Hash(Data) = Digest
Checksum 出自編碼論,可以理解為一種特殊的哈希函式,用來檢查檔案的完整性,換言之,如果一份資料出現了任何變動,Checksum 應該期待會改變,(但是 Checksum 實際上并不要求唯一性,見 Fingerpint)
Fingerprint 類似于 Checksum,但是 Fingerprint 通常更嚴格,它通常要求最少有 64-bit,使得任何兩個檔案只要不同,幾乎(概率意義上接近 2^-64)不可能有同一份指紋,即唯一性,(但是 Fingerprint 的定義不要求密碼安全性即 cryptographic)
所以 Checksum 只是作為檔案變更校驗,而 Fingerprint 可以作為資料的唯一標記,
在命名時,優先使用 Fingerprint/Checksum,或其它特定指定用途的術語,當以上均不合適時,回退到更泛化的概念,即 Digest,
流式編程
Stream Source/Sink Pipe/Piped
流式編程通常有自己的專有詞匯表,具體地:
-
Stream 表示流式 -
Source 表示資料源(輸入),Sink 表示 資料匯(輸出), -
動詞詞匯表,如 map/reduce/join/filter/iterate/do/window/key/evict/peek/trigger/slide/...
原則是:選擇你的團隊里最常使用的流式處理系統所使用的詞匯表,
狀態
State/Status
很諷刺地,很多人認為這兩個詞有區別,但是他們認為區別的點各不相同,見下文參考文獻,筆者傾向于認為它們其實沒什么本質區別,
鼓勵使用 State 表示狀態,因為 HTTP 和 RPC 已經占用了 Status 這個術語,為了避免誤解,使用 State 表示自定義狀態,
參考:
-
https://stackoverflow.com/questions/1162816/naming-conventions-state-versus-status -
https://softwareengineering.stackexchange.com/questions/219351/state-or-status-when-should-a-variable-name-contain-the-word-state-and-w -
鼓勵使用 State: https://google.aip.dev/216
計數
Num/Count/Size/Length/Capacity
-
Num/Count 表示數量,但不強制是某個 collection 的長度,推薦使用 Count, -
Size/Length 表示容器1的當前容量,遵循語言慣例,通常使用 Size, -
Capacity 通常表示容器的最大容量,
方法/動詞
動詞是句子的精髓,選擇精準的動詞是代碼可讀性的關鍵, 本章對動作做了分類,并且提供了部分備選,如果動詞有反義詞,它們會被聚合在一個詞條中, 本章的詞匯有兩種:
-
動詞 -
以執行一個動作為主的某些行為類,即 -er 模式,如 Producer, -able 模式,如 Writable 是類似的,因為不再贅述,
創建/提供
Producer/Provider/Supplier/Generator/Constructor/Factory Builder.build
Verbs:
-
create/from/of/with/valueOf/instance/getInstance/newInstance/getFoo/newFoo -
get/peek -
make/generate
創建/提供名詞辨析:
-
Producer/Supplier/Provider 同義詞,都表示“提供一個物件”,這些提供者可能是惰性的(Lazy),實體未必由這些提供者創建(雖然通常是), -
它們對應的動詞是工廠方法的常見命名,即: -
create/from/of/with/valueOf/instance/getInstance/newInstance/getFoo/newFoo -
推薦在專案中使用同一種命名,推薦使用 of/from,更短, -
Generator 通常專指某些需要經過計算的、特殊的物件,例如 ID, -
對應的動詞是 generate,強調全新生成, -
Constructor 通常是指一個復雜物件的構建器,不是指建構式,它通常用于比 Builder 更復雜的構建 (Builder 通常不會附帶邏輯), -
Factory 是專職的工廠類,當工廠方法較為復雜,需要抽出,或者有狀態的工廠時使用, -
對應上文工廠方法的常見命名, -
Builder 見前文 [Builder 構建者模式]
動詞辨析:
-
get vs peek -
get 是廣義的“獲取”,在絕大部分場景下適用 -
peek 也是“獲取”物件,但是這里強調的是對原物件無副作用,在函式式編程中會用來作為不破壞資料流的旁路操作, -
create vs make vs generate -
同義詞,表創建,推薦在專案中保持一致,
消費
Consumer.accept/consume/poll
消費名詞:
-
Consumer 是最常見的“消費者”,通常表示某個資料流的終端消費方, -
對應的動詞是 accept 或是 consume,遵守所使用訊息佇列框架的命名風格,否則,專案內保持一致, -
poll 特指資料是通過輪詢(poll),即 Consumer 通常主動獲取訊息,而非被推送(push)后處理,
注意區分輪 xun 中文的歧義:
-
poll 翻譯為輪詢,指一個客戶端間斷性地向外進行獲取資料的行為策略, -
round-robin 翻譯為輪循,指以單一的方向回圈接受資訊/資源的分發策略,
注意輪詢是 poll 不是 pull,雖然后者直覺上是“拉取,但 poll 強制間斷性地主動地采樣/獲取資料,是正式的計算機術語,
查找
Verbs: - find/search/query
同義詞,推薦在專案中保持一致, 具體地,這幾個詞實際上有細微的不一致,通常情況下它們可能有以下區分:
-
find 查詢單個結果,search 查詢一列符合條件的結果 -
find 表示“找到”,即終態,search 表“搜索”,即行為, -
query 表示“查詢”,類似于 search,但是暗示可能會有更高的成本, 但是,不要做這種程度的細分,大部分人認為它們是同義詞,
參考 https://stackoverflow.com/questions/480811/semantic-difference-between-find-and-search
拷貝
Verbs: - copy/clone
同義詞,遵循語言慣例,
Java 使用 clone, Go/C++ 使用 copy,
添加
Verbs: - add/append/put/insert/push
動詞辨析:
-
append 專指添加到串列末, -
insert 強調可以插入到串列的任何位置, -
add 是通用的串列添加方案, add(E)等同于append,add(index, E)等同于insert,addAll用于批量添加, -
put 通常用于串列之外的添加場景,如 map, iostream, -
push 僅用于某些資料結構,如堆疊,
對于自定義的可添加 api,應該貼近底層的標準庫的資料結構所使用的動詞,作為泛用的添加,使用 add,
更新
Verbs: - set/update/edit
同義詞,在代碼 API 中使用 set,在 RPC API 中使用 update,
洗掉
Verbs: - remove/delete/erase/clear/pop
動詞辨析:
-
remove/delete/erase 是同義詞,嚴格來說,remove 指移除,即暫時從容器中取出放置在一邊,delete/erase 指洗掉,即將物件整個清除,但是在日常編程中不需要做這種區分,通常,代碼 API 中使用 remove(或依語言慣例),RPC API 中使用 delete 作為標準方法, -
clear 通常表示 1) 清理串列,等效于 removeAll 2)清理狀態,即恢復類到初始化狀態, -
pop 只在堆疊/佇列等資料結構中使用,通常會回傳物件并從資料結構中移除,
編排
Scheduler/Dispatcher/Coordinator/Orchestrator/Delegator - Verb: schedule/dispatch/orchestrate
Scheduler/Dispatcher 均借用于作業系統概念,
名詞辨析:
-
Scheduler: 通常 Scheduler 用于分發中長期 Job,換言之,Scheduler 通常涉及到資源分配, -
對應動詞為 schedule -
Dispatcher: 通常只負責接受事件,采用某些固定的策略分發任務,例如 round-robin,不涉及資源分配, -
對應動詞為 dispatch -
Coordinator: 通常作為 Scheduler/Dispatcher 的同義詞,鑒于其模糊性,推薦使用更細化的 Scheduler/Dispatcher -
對應動詞為 coordinate -
Orchstrator:執行比簡單的分發,即 scheduler/dispatcher 更復雜的任務/流程編排,通常,這些任務有前后依賴關系,會形成一個有向無環圖, -
對應動詞為 orchestrate, Orchestrator 的輸出通常是作業流,即 Workflow, -
Delegator: 專指委任,雖然形式類似,但是 Delegator 強調單純的委任,參見 [Delegation: 委派模式], -
對應動詞為 delegate,但通常不會使用,
檢查/驗證
Validator/Checker/Verifier - Verb: validate/check/verify/assert
Validation/Verification 的明確區分來自于軟體測驗,
-
Validation 通常指對產品符合用戶/顧客預期的驗證,外部用戶會參與, -
Verification 通常指產品合規/符合給定規范,通常是內部流程,
在程式中,不沿用這種區分,通常:
-
Validator 用于輸入檢測 -
Verifier 用于運行的不變數檢測
具體地:
-
check 用于輸入校驗, validate 用于復雜的輸入校驗, -
assert/verify 用于不變數驗證,尤其在單元測驗中
public void process(String s, ComplicatedObject co) {
checkNotNull(s); // check
validateComplicatedObject(co); // validate
}
@Test
public void testProcess() {
process("ss", co);
Truth.assertThat(...); // assert
verifyZeroInvocations(co); // verify
}
執行/操作
Task/Job/Runnable
Executor/Operator/Processor/Runner - Verb: exec/execute/do/process/run
名詞辨析:
-
Runnable 是泛用的“帶上文的可執行代碼塊”, -
Task 粒度比 Job 更細 -
Job 通常是耗時更長的任務
但是,推薦不做區分,認為它們都是同義詞,使用 Task 或者 Job 作為類名,
名詞辨析: Processor/Executor/Operator 是從計算機架構借用的概念,
-
Executor: 常見,通常對應 Job/Task -
對應 execute, exec 是可接受的公認的縮寫, -
Operator: 通常對應某些具體的操作類,更多使用本義,即運算子, -
對應 do, -
Processor:更多在文本檔案(work/document processor)、資料處理(data processor) 語境下使用, -
對應 process, -
Runner: 通常對應 Runnable -
對應 run
但是,推薦不做區分,認為它們都是同義詞,日常編程中,使用 Executor 作為 Job 執行器,
開啟 vs 關閉
toggle/switch/enable/disable/turnOn/turnOff/activate/deactivate
二元狀態的開啟關閉,上述全是同義詞,
在專案中保持統一,注意比起 toggle(bool) 和 switch(bool),更推薦分離的 enable/disable,
讀取 vs 寫入
Reader/Prefetcher/Fetcher/Downloader/Loader - Verb: read/get/fetch/load/retrieve Writer/Uploader - Verb: write/upload
Lifecycle: - open/close
名詞辨析:
-
Reader 通常是從 stdio/檔案/其它 Source 中讀取, -
對應動詞 read -
Fetcher 通常是從遠端拉取資料 -
對應動詞 fetch -
Downloader 類似于 Fetcher,但是通常內容是檔案等 blob,而非結構化資料 -
對應動詞 download -
Prefetcher 強調預熱拉取,通常是拉取到快取中, -
對應動詞 prefetch 或是簡單的 fetch -
Loader 是泛用詞匯,表示廣義的“加載”,通常可以表示上述的任何一種, -
對應動詞 load -
Retrieve 是 Fetch 的同義詞, -
具體地,fetch/load 是有語意的細微差別,但是,不需要做具體的細分,
優先使用 read/fetch/download,當均不合適時,回退到 load,
序列化 vs 反序列化
Serializer - Verb: serialize/pack/marshal Deserializer - Verb: deserialize/unpack/unmarshal
動詞辨析:
-
pack 指打包,將資料打包為一個不可拆分的(通常是不透明的)物件 -
serialize 指序列化,將資料轉換為可以被存盤/傳輸的(通常是二進制)格式, -
marshal 強調意圖 -- 將一個物件從程式 A 轉移到程式 B 中,
但是,不需要做這個區分,可以認為它們都是同義詞,按語言慣例使用:
-
C++: Serialize -
Java: Serialize -
Go: Marshal -
Python: Pack
注意反序列化是 deserialize, 比 unserialize 更常見, 但 pack -> unpack, marshal -> unmarshal,
-
https://en.wikipedia.org/wiki/Marshalling_(computer_science) -
https://en.wikipedia.org/wiki/Serialization
轉換
Applier/Converter/Transformer/Mapper - Verb: apply/convert/transform/map/to/translate
可以認為它們都是同義詞,在專案中應該保持一致, 嚴格來說,Mapper 更多指同一資料的兩種形式的雙向映射,例如資料庫存盤和運行時物件, 在 Applier/Converter/Transformer 中,Applier 最為常見,因為源自設計模式, Mapper 在框架中較常見,
匹配
Filter/Matcher - Verb: query/filter/match
可以認為它們都是同義詞, 在專案中應該保持一致,
事件
Event
Listener/Notifier Verbs: notify Observer/Observable Verbs: observe Handler Verbs: handle Publisher/Subscriber Publisher/Consumer
在 [Observer Pattner: 觀察者模式] 中已經解釋,
-
Observer 是正宗的觀察者 -
Listner/Notifier 通常可以用來作為 Observer/Observable 的同義詞,但是 Listener 也可能表示其它含義,如 TraceListener,視框架而定, -
Handler 也是同義詞,它與 Listener/Observer 的區別在于,它表示唯一的事件處理器,而 Listener/Observer 可能有多個, -
Publisher/Subscriber: 在事件流系統中,表示 1:N 廣播/訂閱, -
Producer/Consumer: 在整個流系統中,專指 1:1 生產/消費,
見 https://stackoverflow.com/questions/42471870/publish-subscribe-vs-producer-consumer
文本處理
Regex/Pattern/Template
Pruner/Stripper/Trimmer Formatter/Prettier Resolver/Parser/Expander
- Verb: compile/parse/resolve/expand - Verb: format/split/separate/merge/join
通常,一個程式中有 20% 的代碼在處理字串,所以與文本相關的內容非常多,這里沒有列出全部,
“模板”名詞決議:
-
Regex 專指正則運算式,另一個常見的縮寫是 Regexp,應該與語言保持一致,C++ 使用 Regex,Go 使用 Regexp, -
編譯使用 compile, 正則本身就是一種 formal language,因此使用 compile 是正統, -
匹配對應動詞為 expand/match/match -
Pattern 在 Java 中表示正則運算式,雖然 Pattern 可能通指“模式”,但是通常不在編程中使用, -
編譯使用 compile -
對應動詞為 match/split -
Template 指模板,通常不是正則形式的,而是簡單的匹配替換模板,如 HTML 模板, -
對應動詞為 expand
“修剪”動名詞決議:
-
Pruner.prune: 指清理掉過時的、不應存在的內容 -
Stripper.strip: 指清理掉多余的、過度生長的內容 -
Trimmer.trim: 泛指修剪,使其更好看,
但是,Prune/Strip/Trim 在編程中通常認為是同義詞,它們通常情況下:
-
Strip/Trim 指去掉頭尾的多余空格 -
Prune 可能會進行其它的裁剪
語言可能會為之賦予特殊含義,例如在 Java 11 中,Trim 會清理掉所有的普通空格,而 Strip 會清理掉所有的 Unicode 空格,
-
https://stackoverflow.com/questions/51266582/difference-between-string-trim-and-strip-methods-in-java-11
“格式化”動名詞決議:
-
Formatter.format 是將物件進行格式化,通用名詞, -
Prettier.pprint 專指將資料整理為便于人類可讀的的輸出格式,典型的例子是 Python 的 pprint,
“決議”動名詞決議:
-
Expander.expand 通常用于 DSL/Schema 決議,專指將某些 DSL 展開,如變數替換,展開 glob, -
Parser.parse 類似于 parse,但強調將文本進行句法決議,形成格式化的中間層表示,借用了編譯器術語, -
Resolver.resolve Resolve 通常指從人類可讀的定義(可能有歧義或不精確)向機器可讀的定義(精確的、可決議的)的轉換,例如,域名 -> ip 的決議,依賴包的版本號的決議(打平),(!) resolve 不同于 expand/parse 的文本決議,這是一個相同中文不同英文的易混淆例子,
生命周期
Lifecycle
Initializer/Finalizer
Verb: - init/setup/prepare - pause/resume - start/begin - end/terminate/stop/halt - destroy/release/shutdown/teardown
生命周期決議: 一個物件的生命周期,稱為 Lifecycle,通常有以下流程:
-
創建,通常由語言特性支持,不屬于生命周期管理范圍, -
初始化:init,init 是 initialize 的全稱,通常用來初始化一個類到可用狀態,應該盡量避免創建之外的額外初始化步驟,一個物件應該盡可能在創建后就處于已初始化狀態,額外的狀態會讓這個類更難正確使用, -
setup/prepare 是 init 的同義詞,應該在專案內統一,推薦為 init,setUp 通常在測驗中使用,用于作為每個測驗用例設計的前置步驟, -
init vs prepare: 具體地細分,init 的語意通常指“在類生命周期層面處在正常可執行狀態”,prepare 的語意通常指“在業務層面做好準備” -
開始: start/begin,通常用于這個物件正式開始正常作業,即切換到 running 狀態,在切換到其它狀態之前這個類會一直保持在 running 狀態, -
start/begin 是同義詞,通常使用 start 作為動詞“開始”,使用 begin 作為串列的頭, -
暫停: pause,pause 應該使得類暫停運行,從 running 狀態切換到 paused 狀態,這段時間這個類應該不再作業, -
恢復:resume,resume 與 pause 是成對的, resume 會恢復 paused 到 running 狀態,通常,pause/resume 可以無限次隨時切換, -
停止:stop/end/terminate/halt,停止類運行,與 start 對應,通常情況下,一個類 stop 意味著不會再重新啟動,通常情況下,停止狀態的類應該拒絕任何請求, -
stop/end/terminate/halt 是同義詞,不要做區分處理,在專案中保持一致, -
銷毀:destroy/release/shutdown/teardown/exit,徹底銷毀物件,此后,物件不再處于可用狀態, -
destroy 強調銷毀物件, -
release 強調釋放資源, -
teardown 通常與 setup 對應, -
exit 通常指程式退出, -
shutdown 是通用“徹底關閉”的動詞,當 destroy/release 不合適時,回退到 shutdown, -
使用 gracefullyShutdown 表示優雅關閉,這通常意味著是前幾個行為的集合:停止服務、釋放資源、重繪緩沖區、銷毀物件, -
destroy/release/shutdown/teardown 是近義詞,具體地:
計算
Calculator
Verb: - compute/calculate/calc
使用 Calculator 而非 Computer 表示某個運算的執行器,Computer 雖然也是“計算器”,但是在代碼語境下有歧義,
compute/calculate/calc 可以認為是同義詞,如果是 Calculator,使用 calculate,其它情況下,使用 compute,
元資料(配置/環境/...)
Option/Config/Configuration/Setting/Preference/Property/Parameter/Argument
Context/Environment
Info/Metadata/Manifest/Version
配置名詞決議: 這個有類似的名詞辨析,但是它們在編程時通常認為都是“配置”的同義詞,它們還會出現在用戶界面,尤其是 Settings/Options/Preferences,
在編程的角度,Option/Config/Configuration 是同義詞,均表示配置,慣例使用 Options 作為子類定義一個類所需的配置,尤其是作為依賴注入時,
使用 Property 表示單個屬性, 而且通常是 k-v 結構,換言之,Option/Config 通常由多個 Properties 組織,只有當 Property 是動態屬性時,才定義特殊的 Property 類,否則,在 Option 中定義具體的域表示 Property,
struct Options {
int fur_layer_count; // Good
int fur_layer_count_property; // Bad! Property unnecessary
struct ColorProperty {
int a;
int r;
int g;
int b;
} // Bad! Prefer Color.
ColorProperty color;
}
引數決議:
-
Parameter:通常表示在介面處定義的引數 -
Argument:指實際傳入介面的引數
例如:
func foo(param string)
foo(arg)
https://stackoverflow.com/questions/156767/whats-the-difference-between-an-argument-and-a-parameter
背景關系名詞辨析:
-
Context 指背景關系,通常用于在 API 之間傳遞與一次執行相關的資訊,在 RPC 處理中非常常見,例如 https://pkg.go.dev/context , -
Environment 指環境,這個名詞從系統環境變數而來,通常,這表示在程式啟動后保持穩定的環境資料,不隨所執行的內容(如 rpc 請求)變化而變化,
元資料辨析:
-
Info 泛指資訊,而元資料相當于特定的“關于資料”的資訊, -
Metadata 標準用語,專指元資料,避免使用 Info 代表元資料, -
Manifest 專指檔案清單,描述一個模塊中的檔案/功能/其它組成結構的串列,Manifest 來自于貨運術語,Ship Manifest 用以列出所有的船員和船隊所有的船只, -
Version 專指程式的版本元資料,例如 TrpcVersion,如果一個類專指版本,使用 Version 是最精確合適的,
作者:marinewu
本文來自博客園,作者:古道輕風,轉載請注明原文鏈接:https://www.cnblogs.com/88223100/p/Programmers-naming-Guide-to-Radical-Treatment-of-Headache.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/547547.html
標籤:其他
上一篇:基于Kafka和Elasticsearch構建實時站內搜索功能的實踐
下一篇:程式員“起名”頭痛根治指南
