《代碼整潔之道》筆記04——數據結構

數據抽象

隱藏變量內部結構,可以通過抽象取值和設置的方法,讓用戶無須關心數據的實現而就能操作數據本體。

例如:

class Point {
    getX:function(){};
    getY:function(){};
 	setX:function(){};
    setY:function(){};
}

當然並不是一味的暴露取值和賦值器就可以完成。

例如:

class Vehicle {
    getFuelTankCapacityInGallons:function(){}
	getGallonsOfGasoline:function(){}
}
class Vehicle {
    getPercentFuelRemaining:function(){}
}

前者很明顯是具象手段與機動車的燃料曾層進行通信;而後者則是通過抽象手段採用百分比來獲得燃料的數據。

前者你很明顯可以發現是變量的存取器;而後者是·並不能知道內部的數據形態。

這裏明顯後者爲佳,因爲我們不願意暴露數據的細節,更願意以抽象形態表述數據。

要以最好的方式呈現某個對象包含的數據,而不是一味的取值器與賦值器來完成。

數據、對象的反對稱性

上面的2個例子表現了對象與數據結構的差異。

  1. 對象把數據隱藏於抽象之後,暴露操作數據的函數。

  2. 數據結構暴露其數據結構,沒有提供有意義的函數

這2者的差距雖小,但是有着深遠的意義。

  1. 過程式代碼

    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具有具體的函數。

    1. 優點:

    ​ 我們無論在Geometry內部中添加什麼方法,都不會影響到外部的形狀類的數據結構。

    1. 缺點:

    ​ 但是如果添加新的新形狀,就需要在Geometry添加新的代碼。

  2. 面向對象的方法

    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 只應該調用以下對象的方法

  1. C
  2. 由於 f 創建的對象
  3. 作爲參數傳遞給 f 的對象
  4. 由C的實體變量持有的對象

換句話而言,方法不應該調用由任何函數返回的對象的方法。只跟自己朋友說話,而不和陌生人說話。

我們通過幾個反例來了解這個定律:

  1. 火車失事

    let opts = ctx.getOptions()
    let scratchDir = opts.getScratchDits()
    let outputDir = scratchDir.getAbsolutePath()
    

    這個很像火車意義,一節接着一節連接着。我們來看看如何違法了這個定律:

    模塊ctx對象包含了許多選項,每一個選項都有許多目錄,每一個目錄對應一個路徑。

    對於一個函數而言,它實在太豐富了。

    如果上面只是數據結構,當然該定律並不適用;而如果是對象的話,它就暴露了對象中的數據結構,不符合我們對象的隱藏數據結構,暴露操作數據的函數。

  2. 混雜

    這種混雜有時候會不幸導致混合結構,一半是對象,一半是數據結構。這種結構可能會同時出現提高添加新數據結構的難度,也出現提高添加新的類的難度。

  3. 隱藏結構

    就拿火車失事的例子來說明。如果我們需要隱藏數據結構同時,又要拿到臨時目錄的絕對路徑,我們該如何實現呢?

    我們首先來看看取得絕對路徑的需求,獲得絕對路徑是爲了獲得創建指定名稱的臨時文件,我們可以直接讓ctx來操作

    let file = ctx.createScratchFileStream(classFileName)
    

    這樣即隱藏了數據結構,也防止了當前函數瀏覽它不該知道的對象。

數據傳遞對象

DTO是最爲精煉的數據結構,只有公共變量,沒有函數的類。這種結構有時候被稱爲數據傳遞對象。

主要用於:數據庫通信或者解析套接字傳遞的消息之類的場景。

小結

  1. 對象暴露行爲,隱藏數據,便於添加新對象類型而不需要修改既有的行爲,同時也難以在既有的對象中添加新的行爲。

  2. 數據結構暴露數據,沒有明顯的行爲,便於再既有的數據結構添加新的行爲,同時也難以向新既有函數添加新的數據結構。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章