中石油网页设计与网站建设武汉seo优化服务
重构数据-Change Value to Reference将实值对象改为引用对象三
1.将实值对象改为引用对象
1.1.实值对象和引用对象区别
下面通过客户Customer和订单Order两个对象介绍下它们的区别
值对象
:当一个客户Customer下了多个订单Order后,每个订单类都将创建一个客户对象,即使多个订单属于同一个客户,但每个Order对象还是拥有各自的Customer对象。所以对于多个订单Order来说没办法共享同一个客户的信息。
引用对象
:同 一客户拥有多份不同定单,代表这些定单的所有Order对象就可以共享同一个Customer对象以及对象中的所有属性信息。
1.2.使用场景
当A和B两个对象之间是1对多关系时,就可以将值对象转为引用对象。实现的方法有很多中,例如单例模式就是其中的一种。
例如上面的客户和订单关系就是1对多关系,多个订单共享同一个客户对象。
1.3.如何做
- 使用Replace Constructor with Factory Method (304)。
- 编译,测试。
- 决定由什么对象负责提供访问新对象的途径。
- 可能是一个静态字典或一个注册表对象。
- 你也可以使用多个对象作为新对象的访问点。
- 决定这些引用对象应该预先创建好,或是应该动态创建。
- 如果这些引用对象是预先创建好的,而你必须从内存中将它们读取出来,那么就得确保它们在被需要的时候能够被及时加载。
- 修改工厂函数,令它返回引用对象。
- 如果对象是预先创建好的,你就需要考虑:万一有人索求一个其实并不存在的对象,要如何处理错误?
- 你可能希望对工厂函数使用Rename Method (273),使其传达这样的信息:它返回的是一个既存对象。
- 编译,测试。
1.4.示例
在Replace Data Value with Object 一节中,我留下了一个重构后的程序,本节范例就从它开始。我们有下列的Customer class:
class Customer {public Customer (String name) {_name = name;}public String getName() {return _name;}private final String _name;}
它被 Order class使用
class Order...public Order (String customerName) {_customer = new Customer(customerName);}public void setCustomer(String customerName) {_customer = new Customer(customerName);}public String getCustomerName() {return _customer.getName();}private Customer _customer;
此外,还有一些代码也会使用Customer对象
private static int numberOfOrdersFor(Collection orders, String customer) {int result = 0;Iterator iter = orders.iterator();while (iter.hasNext()) {Order each = (Order) iter.next();if (each.getCustomerName().equals(customer)) result++;}return result;}
到目前为止,Customer对象还是value object。就算多份定单属于同一客户,但每个Order对象还是拥有各自的Customer对象。
我希望改变这一现状,使得同 一客户拥有多份不同定单,代表这些定单的所有Order对象就可以共享同一个Customer对象。本例中这就意味:每一个客户名称只该对应一个Customer对象。
首先我使用 Replace Constructor with Factory Method
。这样,我就可以控制Customer对象的创建过程,这在以后会是非常重要的。我在Customer class中定义这个factory method:
class Customer {
// 创建一个实例化Customer对象方法,该方法称为工厂方法public static Customer create (String name) {return new Customer(name);}
然后我把「对构造函数的调用」替换成「对factory method的调用」:
class Order {public Order (String customer) {// 调用工厂方法创建Customer对象_customer = Customer.create(customer);}
然后我再把构造函数声明为private:
class Customer {private Customer (String name) {_name = name;}
现在,我必须决定如何访问Customer对象。
我比较喜欢通过另一个对象(例如Order class中的一个值域)来访问它。但是本例并没有这样一个明显的值域可用于访问Customer对象。
在这种情况下,我通常会创建一个注册(登录)对象,作为访问点。为了简化我们的例子,我把Customer对象保存在Customer class的一个static值域中,让Customer class作为访问点:
private static Dictionary _instances = new Hashtable();
然后我得决定:应该在接到请求时创建新的Customer对象,还是应该预先将它们创建好。这里我选择后者。
在应用程序的启动代码(start-up code)中,我先把需要使用的Customer对象加载妥当。这些对象可能来自数据库,也可能来自文件。为求简单起见,我在代码中明确生成这些对象。反正以后我总是可以使用 Substitute Algorithm 来改变它们的创建方式。
class Customer...static void loadCustomers() {new Customer ("Lemon Car Hire").store();new Customer ("Associated Coffee Machines").store();new Customer ("Bilston Gasworks").store();}private void store() {_instances.put(this.getName(), this);}
现在,我要修改factory method,让它返回预先创建好的Customer对象:
public static Customer create (String name) {return (Customer) _instances.get(name);}
由于create()总是返回既有的Customer对象,所以我应该使用Rename Method 修改这个factory method的名称,以便强调(说明)这一点。
class Customer...public static Customer getNamed (String name) {return (Customer) _instances.get(name);}