鍍金池/ 教程/ 區(qū)塊鏈/ 6 幾種 Erlang 的特殊慣例
第 三 章 軟件工程的原則
第 4 章 錯(cuò)誤處理機(jī)制
9 最常見的錯(cuò)誤
6 幾種 Erlang 的特殊慣例
第 5 章 進(jìn)程、服務(wù)及消息
8 文檔
7 特殊的語(yǔ)法規(guī)范
使用 Erlang 編程開發(fā)——編程規(guī)則及規(guī)范
第 二 章 結(jié)構(gòu)和Erlang術(shù)語(yǔ)
10 必備文檔

6 幾種 Erlang 的特殊慣例

6.1 用記錄作為主要的數(shù)據(jù)結(jié)構(gòu)

把記錄作為主要的數(shù)據(jù)結(jié)構(gòu)。記錄是Erlang 4.3 時(shí)才引入的一種帶標(biāo)記的元組(參看 EPK/NP 95:034)。它類似于 C 語(yǔ)言中的 struct 或 Pascal 中的 record。

如果記錄將用于多個(gè)模塊,它的定義應(yīng)放在這些模塊所包括的一個(gè)頭文件中(帶有 .hrl 后綴)。如果記錄只用在一個(gè)模塊中,則它的定義應(yīng)位于定義模塊的文件的最前面位置處。

Erlang 的記錄特性可以保證數(shù)據(jù)結(jié)構(gòu)跨模塊的一致性,因此在模塊間傳遞數(shù)據(jù)結(jié)構(gòu)時(shí),接口函數(shù)應(yīng)使用記錄。

6.2 使用選擇器和構(gòu)建函數(shù)

使用記錄特性所提供的選擇器(selector)和構(gòu)造函數(shù)(constructor)來(lái)管理記錄實(shí)例。不要使用明顯假定記錄是一個(gè)元組的匹配。范例如下:

demo() ->
  P = #person{name = "Joe", age = 29},
  #person{name = Name1} = P,% 使用匹配,或者......
  Name2 = P#person.name. % 像這樣使用 selector  

不要像下面這樣編程:


demo() ->
  P = #person{name = "Joe", age = 29},
  {person, Name, _Age, _Phone, _Misc} = P. % 不要這樣做

6.3 使用帶標(biāo)記的返回值

使用帶標(biāo)記的返回值。

不要像下面這樣編程:

keysearch(Key, [{Key, Value}|_Tail]) ->
  Value; %% Don't return untagged values!
keysearch(Key, [{_WrongKey, _WrongValue} | Tail]) ->
  keysearch(Key, Tail);
keysearch(Key, []) ->
  false.  

{Key, Value} 不含有 false 值。下面是正確的方法。


keysearch(Key, [{Key, Value}|_Tail]) ->
  {value, Value}; %% Correct. Return a tagged value.
keysearch(Key, [{_WrongKey, _WrongValue} | Tail]) ->
  keysearch(Key, Tail);
keysearch(Key, []) ->
  false.  

6.4 慎重使用 catchthrow

千萬(wàn)不要在搞不清用法的情況下使用 catchthrow!要盡可能少地使用它們。

復(fù)雜與非信任輸入(從外部環(huán)境中所輸入的內(nèi)容,而非來(lái)自于可信的程序時(shí))可能會(huì)導(dǎo)致代碼很多深層次位置的錯(cuò)誤,當(dāng)程序處理這些類型的輸入時(shí),catchthrow 就顯得非常有用了。關(guān)于這一點(diǎn),典型的例子就是編譯器。

6.5 慎重使用進(jìn)程字典

千萬(wàn)不要在搞不清用法的情況下使用 getput 等函數(shù)!盡量少用它們。

引入一個(gè)新的參數(shù),就可以重寫使用進(jìn)程字典的函數(shù)。

范例如下:
不要像下面這樣編程:


tokenize([H|T]) ->
  ...;
tokenize([]) ->
  case get_characters_from_device(get(device)) of % Don't use get/1!
    eof -> [];
    {value, Chars} ->
      tokenize(Chars)
  end.  

正確的方法是:


tokenize(_Device, [H|T]) ->
  ...;
tokenize(Device, []) ->
  case get_characters_from_device(Device) of     % This is better
    eof -> [];
    {value, Chars} ->
      tokenize(Device, Chars)
  end.  

使用 getput 會(huì)容易導(dǎo)致,在同樣輸入下函數(shù)的結(jié)果卻隨情況不同而改變。這使得代碼難以閱讀,因?yàn)榇a已經(jīng)失去了確定性。調(diào)試也變得很復(fù)雜,因?yàn)槭褂?getput 的函數(shù)不僅是自身參數(shù)的函數(shù),而且還是進(jìn)程字典的函數(shù)。Erlang 中的很多運(yùn)行時(shí)錯(cuò)誤(比如 bad_match)就包括函數(shù)的參數(shù),但從來(lái)不包括進(jìn)程字典。

6.6 不要使用導(dǎo)入

不要使用 -import,使用它會(huì)讓代碼變得難以閱讀,這是因?yàn)闊o(wú)法弄清函數(shù)定義所在的模塊。使用 exref(交叉引用工具)查找模塊依賴。

6.7 導(dǎo)出函數(shù)

一定要搞清楚某個(gè)函數(shù)之所以導(dǎo)出的原因。這種原因可能包括以下幾種:

  • 該函數(shù)是模塊的用戶接口;
  • 該函數(shù)是面向其他模塊的接口函數(shù);
  • 該函數(shù)從 applyspawn 調(diào)用,但只限于本模塊內(nèi)部調(diào)用。

使用不同的 -export 進(jìn)行分組,并相應(yīng)對(duì)它們進(jìn)行注釋。范例如下:


%% 用戶接口
-export([help/0, start/0, stop/0, info/1]).

%% 模塊間導(dǎo)出
-export([make_pid/1, make_pid/3]).
-export([process_abbrevs/0, print_info/5]).

%% 導(dǎo)出,只限于模塊內(nèi)部使用
-export([init/1, info_log_impl/1]).