在昨天的文章中,講到了類別基礎用法包含了「宣告」、「建構子」及「實體化」,今天會繼續說明Dart class中的其他語法。
如果有學習過Java之類的物件導向程式語言,應該都有聽過封裝(Encapsulation)這個概念,如果要用一句話解釋封裝大概就是:「朕給你的,才是你的﹐朕不給﹐你不能搶。」
封裝基本上就是要確保我們內部的變數或者方法不能被外部(其他人)隨意的調用,在其他語言都會利用 private
protected
public
等語法來決定哪些東西可以給誰用。
但 Dart 沒有這些東西。基本上沒有特別寫都是公開的,大家都可以呼叫及存取。那要怎麼實作私有變數/方法呢?
class Person {
String _age
Person(this._age);
}
就只要以 _
做為命名的開頭就好,就是這麼簡單。
如果是使用DartPad的讀者,應該會發現即使加了_
外部依然存取的到,為什麼?因為Dart private的scope是在package而不是class,簡單來說就是如果是在這隻檔案依然存取的到。
當我們把 Person
移到另外一隻檔案後就會發現我在main裡面的 instance 無法存取到 _name
或_age
那我們要如何取得私有變數呢?以及要如何更改私有變數?
基本上都是利用一個function包裝起來例如:
String getName() => _name;
// 上面是簡寫的arrow function形式,相等於
String getName(){
return _name;
}
那要如何更改私有變數呢?
void setName(String x) {
_name = x;
}
操作起來就會像是這樣
todd.getName() // todd
todd.setName('toddd')
todd.getName() // toddd
也許有人會問如果我將一個變數改成私有變數然後我宣告了getter及setter讓外部可以讀取及更改它,那跟之前公開的狀態有什麼兩樣?
我個人認為是這樣的寫法是可以讓變數的存取或更改都必須要經過一個Function才能進行動作,所以我們可以將每個setter/getter多了一層中間的處理。
而Dart另外提供了方便的語法糖 set
及 get
來進行getter及setter的宣告:
String get getName => _name;
set setName(String x) => _name = x;
看起來只是多加一個關鍵字而已那到底差在哪裡?主要差異會是在調用時的差異:
todd.getName //todd
todd.setName = 'toddd';
todd.getName // toddd
主要就是我們在調用 getter 時不是call function的形式,而是比較像是一般存取屬性時的用法了。而setter也是我們將要set的數值直接放在等號右邊。
但另外要注意的是使用 set
時要注意一定要一個參數不能多也不能少,我覺得這個功能相當等於override掉 =
的功能讓我們在對某些變數做 =
時有抽象的空間。
那到底要直接用Dart提供的set
及 get
還是用function形式呢?我覺得就是看需求及個人習慣了。
所謂的靜態方法/變數意味著我們不需實體化出一個物件我們就能調用了。
宣告起來也很簡單就是在前面加上static即可:
static const type = 'humen';
static void hello() {
print('hello');
}
而在調用時就會是這樣:
Person.hello();
Person.type
我們就不是從 todd
來調用而是直接從類別 Person
進行調用,那這種靜態方法或變數會用在哪裡呢?最常見大概在共用樣式時會用到,我們可能有字體顏色、大小、間距等等眾多數值要管理,總不能設一堆全域變數放這吧?為了方便管理大概就是會建立一個class然後用靜態變數將這些樣式存起來,那之後所有的樣式的源頭都會是這個class我要更改或查看時都會知道要去哪隻檔案看了。而且使用class也比較不怕全域變數污染的問題。
今天的程式碼也有放到github上:
https://github.com/zxc469469/dart-playground/tree/Day06/private-and-static
今天開始介紹了許多跟OOP切身相關的語法,但有可能會因為我對OOP的瞭解不夠深入而導致我少講或講錯了什麼,也歡迎各位讀者在下面留言糾正及討論。
下一篇文章會繼續講解class相關的語法:extends、abstract、mixin。