重構手法之重新組織數據【1】

来源:http://www.cnblogs.com/liuyoung/archive/2017/12/13/7989607.html
-Advertisement-
Play Games

返回總目錄 本小節目錄 Replace Data Value with Object(以對象取代數據值) Change Value to Reference(將值對象改為引用對象) Change Reference to Value(將引用對象改為值對象) 1Replace Data Value w ...


返回總目錄

本小節目錄

1Replace Data Value with Object(以對象取代數據值)

概要

你有一個數據項,需要與其他數據和行為一起使用才有意義。將數據項變成對象

動機

開發初期,你往往決定以簡單的數據項表示簡單的情況。但是,隨著開發的進行,你可能會發現,這些簡單數據項不再那麼簡單了。如果這樣的數據項只有一兩個,你還可以把相關函數放進數據項所屬的對象里;但是Duplicated Code壞味道和 Feature Envy壞味道很快就會從代碼中散髮出來,當這些壞味道開始出現,就應該將數據值變成對象。

範例

下麵有個代表“定單”的Order類,其中一個字元串記錄定單客戶。現在,希望改用一個對象來表示客戶信息,這樣就有充裕的彈性保存客戶地址。信用等級等信息,也得以安置這些信息的操作行為。

class Order
{
    public string Customer { get; }

    public Order(string customer)
    {
        Customer = customer;
    }

    public static int NumberOfOrderFor(List<Order> orders, string customer)
    {
        return orders.Count(order => order.Customer == customer);
    }
}

首先新建一個Customer類來表示“客戶”概念。

class Customer
{
    public string Name { get; }
    public Customer(string name)
    {
        Name = name;
    }
}

現在來重構Order類:

class Order
{
    public Customer Customer { get; set; }

    public Order(string customerName)
    {
        Customer = new Customer(customerName);
    }

    public static int NumberOfOrderFor(List<Order> orders, string customerName)
    {
        return orders.Count(order => order.Customer.Name == customerName);
    }
}

 

小結

以對象取代數據值可以減少重覆代碼。

2Change Value to Reference(將值對象改為引用對象)

概要

你從一個類衍生出許多彼此相等的實例,希望將它們替換為同一個對象。將這個值對象變成引用對象。

動機

 在許多系統中,都可以對對象分類:引用對象和值對象。前者就像“客戶”、“帳戶”這樣的東西,每個對象都代表真實世界中的一個實物,你可以直接以相等操作符(==,用來檢驗同一性)檢查兩個對象是否相等。後者則是像“日期”、“錢”這樣的東西,它們完全由其所含的數據值來定義,你並不在意副本的存在;系統中或許存在成百上千個內容為“1/1/2000”的“日期”對象。當然,你也需要知道兩個值對象是否相等,所以你需要覆寫Equals()以及GetHashCode())。

關於值對象和引用對象的更多介紹,請參考一下鏈接:

http://blog.csdn.net/u011453312/article/details/27269341

http://blog.jobbole.com/99723/

要在引用對象和值對象之間做選擇有時並不容易。有時侯,你會從一個簡單的值對象開始,在其中保存少量不可修改的數據。而後,你可能會希望給這個對象加入一些可修改數據,並確保對任何一個對象的修改都能影響到所有引用此一對象的地方。這時候你就需要將這個對象變成一個引用對象。

範例

看上一節重構後的代碼:

class Customer
{
    public string Name { get; }
    public Customer(string name)
    {
        Name = name;
    }

}
class Order
{
    public Customer Customer { get; set; }

    public Order(string customerName)
    {
        Customer = new Customer(customerName);
    }

    public static int NumberOfOrderFor(List<Order> orders, string customerName)
    {
        return orders.Count(order => order.Customer.Name == customerName);
    }
}

到目前為止,Customer對象還是值對象。就算多份定單屬於同一客戶,但每個order對象還是擁有各自的Customer對象。我希望改變這一現狀,使得一旦同一客戶擁有多份不同定單,代表這些定單的所有Order對象就可以共用同一個Customer對象。

首先使用Replace Constructor with Factory Method,在Customer類中定義一個工廠函數:

public static Customer Create(string name)
{
    return new Customer(name);
}

然後把原本調用構造函數的地方改為調用工廠函數:

class Order
{
    public Customer Customer { get; set; }

    public Order(string customerName)
    {
        Customer = Customer.Create(customerName);
    }

   ...
}

再把構造函數改為私有的:

private Customer(string name)
{
    Name = name;
}

接著在Customer類中聲明一個字典集合,把它保存在Customer類的static欄位中,讓Customer類作為訪問點:

private static Dictionary<string, Customer> _instance = new Dictionary<string, Customer>();

然後在接到請求時事先將Customer創建好。

public static void LoadCustomers()
{
    new Customer("Lemon Car Hire").Store();
    new Customer("Associate Coffee MAchines").Store();
    new Customer("Bilson Gasworks").Store();
}
private void Store()
{
    _instance.Add(Name, this);
}

最後,修改工廠函數,讓他返回預先創建好的Customer對象,並使用Rename Method修改該函數的名稱:

public static Customer GetNamed(string name)
{
    _instance.TryGetValue(name, out Customer customer);
    return customer;
}

3Change Reference to Value(將引用對象改為值對象)

概要

你有一個引用對象,很小且不可變,而且不易管理。將它變為一個值對象。

動機

如果你發現引用對象開始變得難以使用,你就考慮是否應該把它改為值對象。引用對象必須被某種方式控制,你總是必須向其控制者請求適當的引用對象。它們可能造成記憶體區域之間錯綜複雜的關聯。在分佈系統和併發系統中,不可變的值對象特別有用,因為你無需考慮它們的同步關係。

值對象有一個非常重要的特性----它應該是不可變的。無論何時,只要你調用同一個對象的同一個查詢函數,它返回的結果應該是一樣的。如果你可以保證這一點,你可以放心的讓多個對象表示同一個事物。如果值對象是可變的,你就必須確保某一個對象的修改會自動更新到其他代表相同事物的對象中去。那此時你應該使用引用對象了。

範例

class Currency
{
    public string Code { get; }

    private Currency(string code)
    {
        Code = code;
    }
}

這個貨幣類目前是一個引用對象。要把一個引用對象變成值對象,關鍵動作是觀察它是否不可變,如果不是,那就別用此次重構,因為後期的別名同步問題還很煩人。

在這裡Currency是不可變的,所以重寫Equals()和GetHashCode():

public override bool Equals(object obj)
{
    var currency = obj as Currency;
    return currency != null &&
           Code == currency.Code;
}

public override int GetHashCode()
{
    return Code.GetHashCode();
}

完成這個之後我們進行編譯,測試。現在我們想創建多少個Currency都沒問題,我們還可以把他的構造函數聲明為public,我們可以直接用構造函數來獲取Currency實例,從而去掉工廠函數和控制實例創建的行為。

小結

我們一直說值對象是不可變的。那麼什麼是不可變?不可變意味著如果你以Money來表示金錢,那麼Money通常是一個不可變的值。這並不是意味著你的薪資不能改變。而是意味著如果你改變薪資,你必須用另一個對象來替換現在的Money對象,而不是在現在的Money對象上做修改。你和Money對象之間的關係誒改變,但Money對象自身不能改變。

 

To Be Continued……


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 題目描述 如題,已知一個數列,你需要進行下麵兩種操作: 1.將某一個數加上x 2.求出某區間每一個數的和 輸入輸出格式 輸入格式: 第一行包含兩個整數N、M,分別表示該數列數字的個數和操作的總個數。 第二行包含N個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。 接下來M行每行包含3個整數, ...
  • 最近從百度雲盤上下載了一批視頻,然而這些視頻的文件名都帶有廣告。有潔癖的我看著感覺難受,於是想修改過來。但是一個個的修改文件名又嫌麻煩,聯想到業餘時看過的python,於是用python寫了幾行代碼輕鬆地批量修改了文件名稱。 代碼如下: python學習起來著實有趣,聽說python明年在某個省份要 ...
  • 類型檢查 創建類的實例時,該實例的類型為類本身: 要測試實例是否屬於某個類,可以使用type()內置函數: 當然,python中不建議如此檢查,更好的辦法是使用內置類型檢查函數isinstance(obj, cls): 同樣的,內置函數issubclass(cls1, cls2)可以用做子類的檢查: ...
  • 背景: 以前學的Java進行開發,多用到Mybatis,Hiberante等ORM框架,最近需要上手一個C#的項目,由於不是特別難,也不想再去學習C#的ORM框架,所以就想著用反射簡單的實現一下ORM框架的內容,簡單的增刪改查,沒有用到多表之間的聯繫。 反射: Java和C#中的反射大體相同,主要是 ...
  • 由於自己的公司的項目需要調用視頻地址 1:當為鏈接時:直接在播放器用資料庫查找的地址 2:當為外部鏈接時:直接用window.location.href('資料庫查找的地址') 3:當為H5鏈接時:使用<ifram src="資料庫查找的地址">播放 4:當為其餘網站鏈接時,要去第三方網站讀取jso ...
  • 瞭解會話的產生過程、會話的特性、簡單的Cookie、在URL中內嵌會話ID以及這樣做帶來什麼後果 ...
  • spring boot 使用 starter 解決了很多配置問題, 但是, 他是怎麼來解決這些問題的呢? 這裡通過一個簡單的例子, 來看一下, starter是怎麼來設置預設配置的. 一. 建 starter 項目 自定義的starter, 項目命名規範是 : 自定義名-spring-boot-st ...
  • 無繼承 有 static 修飾 static final static 非 final 結果 這裡使用了 OpenJDK 的 JMH 基準測試工具來測試的,結果如下: 總結:你說final的性能比非final有沒有提升呢?可以說有,但幾乎可以忽略不計。如果單純地追求性能,而將所有的方法修改為 fin ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...