物件導向的基本原則之一:單一職責原則,每一個類別裡它的職責應該只有一個,那麼在這樣的設計底下,很有可能類別只有一個函數。
例如:
class Logger{
String message;
Logger(this.message);
void sendMessage(){
print('Sending message: $message');
}
}
呼叫的時候,就必須帶入函數的名稱。
void main(){
var logger = Logger('This is sample log');
logger.sendMessage();
}
//This is sample log
Dart 提供了一個特別的方法,如果類別裡面有一函數的名稱為 call
,那麼該函數則不需要名稱就可以呼叫。
class Logger{
String message;
Logger(this.message);
void call(){
print('Sending message: $message');
}
}
void main(){
var logger = Logger('This is sample log');
logger();
}
//This is sample log
這樣一來,便可以把類別轉換成可以呼叫的樣子。(A class can be called like a function)
當然,我們也是可以用 call()
來呼叫
void main(){
var logger = Logger('This is sample log');
logger.call();
}
//This is sample log
有些函數的呼叫需要輸入不同的參數,根據不同的參數,我們可以得到不同的結果。
如上面的範例,我們將參數轉成類別的屬性,當呼叫類別的 call()
時,就可以直接取用。
舉一個 Flutter 的範例:
有一個類別,專門用來儲存圖片的位置,在該類別裡面包含了一個 call()
函數,我們可以直接呼叫將圖片的位置轉換成圖片。
class DisplayImage{
String imagePath;
String(this.imagePath);
Image call(){
return Image.asset(path, fit: BoxFit.cover);
}
}
DisplayImage('image path')();
假設沒有使用 call()
函數:
class DisplayImage{
String imagePath;
String(this.imagePath);
Image toImageWidget(){
return Image.asset(path, fit: BoxFit.cover);
}
}
DisplayImage('image path').toImageWidget();
toImageWidget()
來將圖片路徑轉成 Image Widget,看起來比較冗長一點。一個類別只有一個 call()
方法,但是除了 call()
之外,還是可以使用其他的方法名稱,只不過就沒有辦法不使用函數名稱來呼叫。
call()
函數也可以帶參數。
class Logger{
void call(String message){
print('Sending message: $message');
}
}
Callable class 讓我們可以思考的是,類別中的哪一個函數可以被放在 call()
函數中。我認為 call()
函數放的是這個類別主要的工作。當呼叫者呼叫時,就可以清楚知道這個類別的主要工作是什麼。不需要多餘的函數名稱來誤導呼叫者。
那麼,如果類別裡只有一個函數,該用頂層函數 (top-level function) 還是 Callable class 呢?如果函數的工作需要跟類別的屬性有關係,用 Callable class 比較適合;反之,可以使用頂層函數。不過這個答案沒有絕對,完全取決於程式設計方向。