原文︰Refactoring Day 1 : Encapsulate Collection
譯文︰31天重构学习笔记1. 封装集合

原始程式碼︰

public class Order
{
    private List<OrderLine> _orderLines;
    private double _orderTotal;

    public IList<OrderLine> OrderLines
    {
        get { return _orderLines; }
    }

    public void AddOrderLine(OrderLine orderLine)
    {
        _orderTotal += orderLine.Total;
        _orderLines.Add(orderLine);
    }

    public void RemoveOrderLine(OrderLine orderLine)
    {
        orderLine = _orderLines.Find(o => o == orderLine);

        if (orderLine == null)
            return;

        _orderTotal -= orderLine.Total;
        _orderLines.Remove(orderLine);
    }
}

public class OrderLine
{
    public double Total { get; private set; }
}

本 Tip 的重點,是希望落實「封裝」的觀念,一個 class 在設計上,應該不提供不需要的操作給使用場景端。

以本文中舉的訂單 (Order) class 做例子,OrderLines 屬性雖然定義為唯讀 (只有 get 操作子),但因為 OrderLines 的型別是 IList<OrderLine>,所以場景端在取得訂單資料時,仍然可以操作訂單的內容。因為我們希望訂單內容的操作,是集中在 Order class 進行,因此這樣的設定可能會帶來一些不必要的隱憂。

根據這樣的理由,我們可以將 OrderLines 型別,調整為 IEnumerable<OrderLine>,確保場景端僅能唯讀訂單資料,而不能修改內容。

public class Order
{
    // ...
    public IEnumerable<OrderLine> OrderLines
    {
        get { return _orderLines; }
    }
    // ...
}

// ...

當然這次的重構比較單純,我們可以直接修改程式中 property 的型別。如果使用 ReSharper,我們可以將編輯的游漂,移到 OrderLines property 上,按下 ctrl + shift + r 的熱鍵︰

接著我們選擇 Change Signature 選項,ReSharper 會跳出以下視窗︰

修改上圖紅框中的 Return type 欄位,將欄位值由 IList<OrderLine> 改為 IEnumerable<OrderLine>,再按下 Next 鍵,如此重構就完成了。

本篇說明的觀念算是相當簡單,下篇我會對應原系列中的移動方法 (Move Method) 的重構技術,記錄如何使用 ReSharper 進行對應動作。