數據抽象
隱藏變量內部結構,可以通過抽象取值和設置的方法,讓用戶無須關心數據的實現而就能操作數據本體。
例如:
class Point {
getX:function(){};
getY:function(){};
setX:function(){};
setY:function(){};
}
當然並不是一味的暴露取值和賦值器就可以完成。
例如:
class Vehicle {
getFuelTankCapacityInGallons:function(){}
getGallonsOfGasoline:function(){}
}
class Vehicle {
getPercentFuelRemaining:function(){}
}
前者很明顯是具象手段與機動車的燃料曾層進行通信;而後者則是通過抽象手段採用百分比來獲得燃料的數據。
前者你很明顯可以發現是變量的存取器;而後者是·並不能知道內部的數據形態。
這裏明顯後者爲佳,因爲我們不願意暴露數據的細節,更願意以抽象形態表述數據。
要以最好的方式呈現某個對象包含的數據,而不是一味的取值器與賦值器來完成。
數據、對象的反對稱性
上面的2個例子表現了對象與數據結構的差異。
-
對象把數據隱藏於抽象之後,暴露操作數據的函數。
-
數據結構暴露其數據結構,沒有提供有意義的函數
這2者的差距雖小,但是有着深遠的意義。
-
過程式代碼
class Square { point:{x:9,y:0} side:20 } class Rectangle { point:{x:9,y:0} height:10; width:10; } class Geometry { Pi: 3.14 area(shape){ if(shape instanceof Square) { let s = shape return s.side * s.side } if(shape instanceof Rectangle) { let s = shape return s.height * s.width / 2 } throw new Error('不存在該圖像') } }
形狀類只具有數據,而
Geometry
具有具體的函數。- 優點:
我們無論在
Geometry
內部中添加什麼方法,都不會影響到外部的形狀類的數據結構。- 缺點:
但是如果添加新的新形狀,就需要在
Geometry
添加新的代碼。 -
面向對象的方法
class Square { point:{x:9,y:0} side:20 area:function(){} } class Rectangle { point:{x:9,y:0} height:10; width:10; area:function(){} }
每一個形狀類中都有自己的函數,這樣的函數好處明顯與前者相反,在新添加一個形狀類的時候,其他類的函數不會受到影響。
這其中體現了數據結構與對象的二分原理:
過程式代碼(使用數據結構的代碼)便於在不改動既有的數據結構前提下添加新的函數;而面向對象代碼便於在不改動既有函數的前提下添加新的類。
反過來看也說得通:
過程式代碼難以添加新數據結構,因爲必須修改所有函數。面向對象難以添加新的函數,因爲需要修改所有類。
得墨忒爾律模塊不應瞭解它所操作對象的內部情況
對象隱藏數據,暴露操作。這意味着對象不應該通過存取器來暴露操作,因爲這樣也會暴露其數據結構。
得墨忒爾認爲:類C的方法 f 只應該調用以下對象的方法
- C
- 由於 f 創建的對象
- 作爲參數傳遞給 f 的對象
- 由C的實體變量持有的對象
換句話而言,方法不應該調用由任何函數返回的對象的方法。只跟自己朋友說話,而不和陌生人說話。
我們通過幾個反例來了解這個定律:
-
火車失事
let opts = ctx.getOptions() let scratchDir = opts.getScratchDits() let outputDir = scratchDir.getAbsolutePath()
這個很像火車意義,一節接着一節連接着。我們來看看如何違法了這個定律:
模塊
ctx
對象包含了許多選項,每一個選項都有許多目錄,每一個目錄對應一個路徑。對於一個函數而言,它實在太豐富了。
如果上面只是數據結構,當然該定律並不適用;而如果是對象的話,它就暴露了對象中的數據結構,不符合我們對象的隱藏數據結構,暴露操作數據的函數。
-
混雜
這種混雜有時候會不幸導致混合結構,一半是對象,一半是數據結構。這種結構可能會同時出現提高添加新數據結構的難度,也出現提高添加新的類的難度。
-
隱藏結構
就拿火車失事的例子來說明。如果我們需要隱藏數據結構同時,又要拿到臨時目錄的絕對路徑,我們該如何實現呢?
我們首先來看看取得絕對路徑的需求,獲得絕對路徑是爲了獲得創建指定名稱的臨時文件,我們可以直接讓
ctx
來操作let file = ctx.createScratchFileStream(classFileName)
這樣即隱藏了數據結構,也防止了當前函數瀏覽它不該知道的對象。
數據傳遞對象
DTO是最爲精煉的數據結構,只有公共變量,沒有函數的類。這種結構有時候被稱爲數據傳遞對象。
主要用於:數據庫通信或者解析套接字傳遞的消息之類的場景。
小結
-
對象暴露行爲,隱藏數據,便於添加新對象類型而不需要修改既有的行爲,同時也難以在既有的對象中添加新的行爲。
-
數據結構暴露數據,沒有明顯的行爲,便於再既有的數據結構添加新的行爲,同時也難以向新既有函數添加新的數據結構。