嵌入式文件
2013 年 6 月 4 日
這些日子,我愈來愈常看到 JSON 資料結構流經伺服器。JSON 文件可以直接儲存,方法是使用 AggregateOrientedDatabase 或關係資料庫中的 序列化 LOB。JSON 文件也可以直接提供給網路瀏覽器,或用於將資料傳輸至伺服器端頁面渲染器。當 JSON 以這種方式使用時,我聽見人們說,使用物件導向語言會造成阻礙,因為 JSON 需要轉譯成物件,然後再重新渲染出來,這浪費了程式設計的功夫 [1]。我同意浪費這一點,但我認為這不是物件的問題,而是不了解封裝所致。
想像一下,我們將訂單儲存為 JSON 文件,並透過輕微的伺服器端處理提供服務,同樣以 JSON 呈現。範例文件可能是這樣。
{ "id": 1234, "customer": "martin", "items": [ {"product": "talisker", "quantity": 500}, {"product": "macallan", "quantity": 800}, {"product": "ledaig", "quantity": 1100} ], "deliveries": [ { "id": 7722, "shipDate": "2013-04-19", "items": [ {"product": "talisker", "quantity": 300}, {"product": "ledaig", "quantity": 500} ] }, { "id": 6533, "shipDate": "2013-04-18", "items": [ {"product": "talisker", "quantity": 200}, {"product": "ledaig", "quantity": 300}, {"product": "macallan", "quantity": 300} ] } ] }
我們假設沒有太多伺服器端處理工作要做,但還是有一些。我們也假設我們使用的是 OO 語言。一個天真的做法可能是讀取 JSON 文件,將資料轉換成適當的物件圖形(包含訂單、品項和交貨),套用任何處理,然後將物件圖形序列化為 JSON 提供給客戶端。
在許多這種情況下,更好的做法是將資料保留在 JSONish 形式,但仍用物件包裝起來,以協調操作。大多數程式設計環境提供通用函式庫,可以取得文件並將其反序列化為通用資料結構。因此,JSON 文件會反序列化為清單和字典的結構,XML 文件會反序列化為 XML 節點的樹狀結構。然後,我們可以取得這個通用資料結構,並將其放入訂單物件的欄位中,以下是一個使用 Ruby 和 JSON 的範例。
class Order...
def initialize jsonDocument @data = JSON.parse(jsonDocument) end
當我們想要操作資料時,我們可以像往常一樣在物件上定義方法,並透過存取這個資料結構來實作它們。
class Order...
def customer @data['customer'] end def quantity_for aProduct item = @data['items'].detect{|i| aProduct == i['product']} return item ? item['quantity'] : 0 end
這包括具有更複雜邏輯的情況。 [2]
class Order...
def outstanding_delivery_for aProduct delivered_amount = @data['deliveries']. map{|d| d['items']}. flatten. select{|d| aProduct == d['product']}. inject(0){|res, d| res += d['quantity']} return quantity_for(aProduct) - delivered_amount end
內嵌文件可以在傳送給客戶端之前進行豐富化。
class Order...
def enrich @data['earliestShipDate'] = @data['deliveries']. map{|d| Date.parse(d['shipDate'])}. min. to_s end
如果需要,您可以在內嵌文件的子樹上建立類似的物件。
class Order...
def deliveries @data['deliveries'].map{|d| Delivery.new(d)} end
類別 Delivery
def initialize hash @data = hash end def ship_date Date.parse(@data['shipDate']) end
這裡要注意的一點是,此類物件封裝器與一般物件並不完全相同。上述程式碼片段中傳回的傳送物件,其相等語意與您從以更常見結構排列的物件中預期的不同。
儘管比較少見,內嵌文件非常適合物件導向。封裝資料的重點在於隱藏資料結構,因此物件使用者並不知道或不關心訂單的內部結構。
熟悉函式程式設計的人會認出將通用資料結構傳遞至一系列函式的風格 - 您可將物件視為提供一個用於處理通用資料結構的命名空間。
內嵌文件的最佳應用時機是,當您以從資料儲存取得的相同格式提供文件,但仍想對該資料進行一些處理時。如果您不需要存取 JSON 文件的內容,則甚至不需要將其反序列化成通用資料結構。訂單物件只需要一個建構函式和一個用於傳回其 JSON 表示形式的方法。另一方面,當您對資料進行更多工作時 - 更多伺服器端邏輯,轉換成不同的表示形式 - 那麼值得考慮是否將資料轉換成物件圖表會比較容易。