面向物件程式設計/狀態之惡
外觀
< 面向物件程式設計
我們沒有足夠強調的一點:狀態(與變化相反)是邪惡的!或者,(也許更好的說法)維護不必要的狀態是所有(呃……許多)錯誤的根源。讓我們看一個例子,現在用 Ruby
- 我們正在隨時間顯示某些內容,因此我們正在處理
- 畫素和秒。根據縮放級別,
- 它們之間存在相關性:每秒畫素,
- 或 PPS。
類時間線
{
PPS;
current_position_in_seconds;
current_position_in_pixels;
public:
GetPosInSeconds() {current_position_in_seconds;}
SetPosInSeconds(s) {current_position_in_seconds = s;} # oops, we're out of sync
GetPosInPixels() {current_position_in_pixels;}
SetPosInPixels(p) {current_position_in_pixels = p;} #oops, we're out of sync
} 在此示例中,我們正在維護不必要的狀態——也就是說,我們同時以秒和畫素來儲存位置。這對使用此類的使用者來說很方便,也很重要,但我們在封裝方面搞砸了。每當您在一個單位中設定值時,都會破壞另一個單位的正確性。您說,這是一個簡單的修復方法
- 我們正在隨時間顯示某些內容,因此我們正在處理
- 畫素和秒。根據縮放級別,
- 它們之間存在相關性:每秒畫素,
- 或 PPS。
類時間線 {
PPS; current_position_in_seconds; current_position_in_pixels;
public
GetPosInSeconds() {current_position_in_seconds;}
SetPosInSeconds(s)
{
current_position_in_seconds = s;
current_position_in_pixels = s*PPS;
}
GetPosInPixels() {current_position_in_pixels;}
SetPosInPixels(p)
{
current_position_in_pixels = p;
current_position_in_seconds = p/PPS;
}
} 這修復了前面示例中明顯的錯誤,但您為自己製造了很多工作。現在您必須在 Timeline 類中的每個方法中保留所有計算的副本以保持一致性。這裡可能還有幾十個這樣的情況(相信我)。那麼,當您新增其他單位時,例如英寸?您必須再次複製所有內容。大概您會明白這將走向何方:計算屬性、邏輯屬性、虛擬屬性,無論您喜歡怎麼稱呼它們。讓我們再次看看這個例子
類時間線 {
PPS; # Pixels Per Second IPS; # Inches Per Second current_position_in_seconds;
public
GetPosInSeconds() {current_position_in_seconds;}
SetPosInSeconds(s) {current_position_in_seconds = s;}
GetPosInPixels() {current_position_in_seconds*PPS;}
SetPosInPixels(p) {current_position_in_seconds = p/PPS; }
GetPosInInches() {current_position_in_seconds*IPS;}
SetPosInInches(i) {current_position_in_seconds = i/IPS; }
} 現在,我們假設 PPS 和 IPS 的轉換因子設定正確,以方便起見,但除此之外,我們已經大大簡化了問題。在我們現在編寫的另外二十個函式中,我們只需要擔心秒,沒有一致性問題需要擔心。此外,如果我們需要將基本單位從秒更改為畫素,Timeline 使用者永遠不會知道。