您的位置:首页 > 其它

重构手法之重新组织数据【1】

2017-12-13 11:34 363 查看

返回总目录

本小节目录

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())。

关于值对象和引用对象的更多介绍,请参考一下链接:

https://www.geek-share.com/detail/2612804961.html

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……

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: