良好程式碼的優點大同小異。
不好的程式碼的糙點卻各有巧妙之處。
-- 台南原地方法院,最棒的古蹟修復案例之一。基本的從外觀的牆面油漆去除,到馬薩式屋頂的木構造修復。建築最美的雙重圓頂與大廳裝飾,都原汁原味的重現在現在的古蹟內。不只是外觀修復得很美,連內部的構造都依原本的構想,聽說是以零件抽離一件修復一件的方式修復,以確保正確性。也是台南唯一開放貓道,可以參觀馬薩式屋頂木構造的古蹟,長達八年的修復已完成,現在是台南司法博物館。
在說了什麼多的「不要這麼做」之後,偶爾也來寫一下「那要怎麼做」比較好。
這次花一點篇幅來介紹 function 怎麼寫,比較好。(好像語法工匠)
在《忍者》[1]第一版中,直接就指出 JavaScript 的 function 有 4 種
(ES6 之後,加上的 arrow function ,請看《忍者2》)
這是一個好語法的心法。
- Its return value is the same for the same arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams from I/O devices).
- Its evaluation has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or I/O streams).
意思是說
盡可能的讓 getter/setter 寫成 pure function ,將有助於你 debug。
這樣的設計,是 flux 在對狀態做更新與取用時,特別要注意的。(ex: vuex 的 mutation, getter)
要不要 return 這件事,其實是值得討論的。
而且,我們其實早就習慣 return 了。
高中數學有學過
y = sin(x)
假想一下 sin()
的實作會是長怎樣?
function sin (radians) {
const PI = 3.1415926588;
while (parseInt(radians % PI)) {
//...
}
//...
return opposite / hypotenuse
}
有 return 將斜邊與對邊相除之後的比例,回傳出來。
換句話說,比例 = sin
是不是等同於 y = sin(x)
的語意呢?
如果今天,我們寫了這樣正確的 sin
函數,但是命名不寫 sin
寫了 ChrisCalculated()
這樣是不是完全看不出來,函數實作內容了?如果不了解 sin 定義的人,看了也一定不懂裡面就是 sin 函數。
有時要不要 return 的問題,其實是命名的問題。
請務必隨時檢查整體概念性
在 C++ 中,兩個輸出很簡單,輸出處傳入指標或參考即可。
兩者在函數的修改,都會影響其變數本體,而不是副本。
void Fun(input i1, output* o1, output* o2) {
}
void Fun(input i1, output& o1, output& o2) {
}
在 JavaScript 中,就將輸出變成物件再傳入。
function Fun(input i1, {output o1, output o2}) {
}
這兩種都不是好方法,因為變數傳入函數之後,一般來說都是預設不會修改其變數本身,或者它的修改都不會影響傳入參數之前的樣子 (傳入副本)。
這樣做確實會產生某些「變數與函數之間的耦合性」要特別寫註解告知伙伴。
Fun(i1, {o1, o2}) // o1, o2: Fun1 的執行結果
Chris大大您好:
我是剛踏入職場的新鮮人,看了你的文章,我非常喜歡,
我也是希望自己的程式碼能越來越好,不要很糙,我有幾個關於設計類別的問題想提問,
就是在function傳入參數,是要使用function時傳入參數,還是要在創立類別時,利用建構子將參數設為欄位,執行function時,使fuction去抓取類別的欄位。
以及function結束時,是否要回傳結果,或者是將結果設為類別的欄位並取得。
關於兩種方式的使用時機,我實在不會拿捏,想問一下是否有各自的使用時機,還是依照個人的撰寫習慣呢?
希望大大能幫忙解答,謝謝~
我稍微整理一下你的問題如下。(另外,我假設你的 function 指的是物件裡的 method,不是全域的 function)
問題一,在function傳入參數
問題二,function結束時
問題很難回答,有幾個面向可以考量
補充一件事
希望有回答到你的問題,如果還有疑問,可以再繼續問哦。
可以的話可以寫一些 sample code 來討論或者描述你的問題看看要怎麼寫比較適合。
抱歉,回覆的時間晚了。
假設不談論抽象的可能性,以簡單的程式舉例來說:
class MemberService_A
{
public bool Login(string account,string password)
{
if (/*Check account & password...*/)
{
return true;
}
return false;
}
}
class MemberService_B
{
string Account { get; }
string Password { get; }
public bool Result { get; set; }
public MemberService_B(string account, string password)
{
Account = account;
Password = password;
}
public void Login()
{
if (/*Check Account & Password...*/)
{
Result = true;
}
Result = false;
}
}
static void Main(string[] args)
{
MemberService_A menberA = new MemberService_A();
MemberService_B menberB = new MemberService_B("account", "password");
bool Logined_A = menberA.Login("account", "password");
menberB.Login();
bool Logined_B = menberB.Result;
}
這兩種都可以辦到相同結果。但不清楚哪種寫法是對於程式比較好的,又或者是兩種其實大同小異,不用專牛角尖哪種寫法。
還有,你指的"表達出「操作真實世界事物」的感覺"是甚麼意思,我有點不明白
MemberService
這真的是很好的例子。
A 和 B 都做一樣的事的話,這兩種設計之間的差異。
是不是 Login 之後,才可以啟動所有的服務,是關鍵。
B 我會改寫成這樣 (之類的寫法)
class MemberService_B
{
public MemberService_B(string account, string password, bool *Result)
{
Result = Login(account, password)
}
bool Login(string account,string password)
{
if (/*Check account & password...*/)
{
return true;
}
return false;
}
}
或者
class MemberService_B
{
public bool Result { get; set; }
public MemberService_B(string account, string password)
{
Result = Login(account, password)
}
bool Login(string account,string password)
{
if (/*Check account & password...*/)
{
return true;
}
return false;
}
}
使用上,就會像這樣
bool Logined_B;
MemberService_B menberB = new MemberService_B("account", "password", Logined_B);
或者
MemberService_B menberB = new MemberService_B("account", "password");
bool Logined_B = menberB.Result;
以 function 的 interface 來說的話
MemberService_A
的意思包含「就算不 Login 也可以提供其它的功能」,Login 前也可以使用某些 methodMemberService_B
傳達了「絕對要 Login 才可以使用」的意思,否則就算建構出來呼叫任何的 method 也沒辦法 work。「操作真實世界事物」的感覺呀!你的例子就應該有這種感覺。
就像是你舉的例子中的 MemberService
,某個服務,提供了哪些功能或可以操作的方式。
而不是一直在堆疊呼叫資料庫或條件判斷、迴圈的程式碼細節。
謝謝Chris大大的詳細解答,讓我了解其中的設計理念與差別,解開我心中疑惑。
希望未來還能繼續拜讀大大您的文章,因為這些文章都讓我受益良多,也希望未來有疑惑還能請教大大您,謝謝!