(本文思想基本来自于经典著作《重构》一书)
上一篇 http://www.cnblogs.com/ceys/archive/2012/03/05/2379842.html#commentform
上一篇文章主要讲了怎么给函数整容。现在我们大家基本上都使用面向对象语言,什么样的“对象”才是优美的呢?
类中的函数、字段应该和该类最紧密相关,如果和另一个类有更多交互,搬移它。搬移字段时,如果很多函数引用了该字段,可以把该字段用get/set方法自我封装起来。这样搬移时只需要修改get/set访问函数。
一个类应该是一个清楚的抽象,处理一些明确的责任。否则建立一个新类,用以表现从旧类中分离出来的责任。提炼类是改善并发程序的一种常用技术,因为提炼后可以给两个类分别加锁。需要注意,提炼出新类,要考虑要不要对用户公开这个新类。如果允许任何用户修改其对象的任何部分,它就成为引用对象;如果不允许任何人不通过对象A修改它的任何部分,则可以将其设为不可修改的,或为它提供不可修改的接口,将A中所有与其相关的函数委托之,从而完全隐藏这个类。
每个对象应该尽可能少了解系统的其它部分,当客户通过一个委托类调用另一个对象,在服务类上建立客户需要的所有函数,隐藏委托关系,减少耦合。然而,如果受托类的功能越来越多,服务类就完全变成了一个中介。很难说隐藏到什么程度是合适的。
当需要为服务类提供一些额外函数,但无法修改这个类时,建立一个新类,使它包含这些额外函数,成为源类的子类或包装类。如果函数较少,直接引入外加函数即可。
子类的工作量比较少,但它有两个问题:第一,必须在对象创建期实施,如果对象创建之后,就不能用了;第二,子类化会产生一个子类对象,如果有其它对象引用了旧对象,就同时又两个对象保存了原数据,如果原数据允许被修改,一个修改动作无法同时改变两份副本。这个时候就要用包装类。包装类需要为原始类的所有函数提供委托函数。
面向对象语言有一个有用的特征:允许定义新类型。这样我们可以利用对象,组织数据。
如果有一个数据项,需要与其他数据和行为一起使用才有意义,将数据项变为对象。
如果一个类衍生出许多彼此相等的实例,希望将它们替换为同一个对象,将这个值对象变成引用对象。每个引用对象都代表真实世界中的一个事物,可以以==检查两个对象是否相等。值对象由其所含数值定义,不在意副本存在。这里举个简单例子:
值对象:
class Customer { public Customer(String name) { _name = name; } public String getName() { return _name; } private final String _name;}class Order { public Order(String customerName) { _customer = new Customer(customerName); } //set,get... private Customer _customer; //other function use Customer...}这里,就算多分订单属于同一客户,每个Order对象还是拥有各自的Customer对象。现在把它改成引用对象,即每个客户名称只对应一个Customer对象:
class Customer { public static Customer getNamed (String name) { return (Customer) _instances.get(name); } private Customer (String name) { _name = name; } private static Dictionary _instances = new Hashtable(); static void loadCustomers() { new Customer("Gang Li").store(); } private void store() { _instances.put(this.getName(), this); } //...}class Order { public Order (String customer) { _customer = Customer.getNamed(customer); } //...} 我们首先创建工厂函数,以控制Customer对象的创建过程。然后,需要决定如何访问Customer对象,可以利用另一个对象访问,但这里Order类没有一个明显的字段可用于访问,所以可以创建一个对象保存所有Customer对象。这里为了简化,利用Customer里的静态字典保存。让Customer类作为访问点。然后,决定何时创建Customer对象。这里为了简化,在loadCustomer里预先把需要使用的Customer对象创建好。
引用对象可能造成内存区域之间错综复杂的关联。在分布系统和并发系统中,不可变的值对象特别有用,因为无需考虑他们的同步问题。这里的不可变指的是对象自身不能改变,比如money对象。当判断相等时需覆写equals()和hashCode()。(实现hashCode的简单办法是把equals()使用的所有字段安慰异或操作)。
如何写出优美的代码(二)
TAG: