标签:
当你在一个类中使用字段的时候,发现这个字段必须要和其他数据或者行为一起使用才有意义。你就应该考虑把这个数据项改成对象。在开发初期,我们对于新类中的字段往往会采取简单的基本类型形式来保存,但随着我们开发进度的增加,这些简单的数据项就不再那么简单了。比如一开始你会使用一个字符串来表示一串电话号码,但是随后你会发现,这个电话号码已经变的不再纯粹,它可能还需要“格式化”,“抽取取号”等特殊行为。一开始你可能会不以为意,觉得这个数据项就这么一两个,不会对你造成影响。但重复代码(Duplicate Code)和依恋情结(Feature Envy)这两个坏味道会很快就散发出来。什么样的行为就应该放在什么样的类中去,当这些坏味道出现的时候你就应该将数据值改为对象了。
例子:
class Order { public: Order(const QString &customer) : m_customer = customer; QString customer() { return m_customer; } void setCustomer(const QString &customer) { m_customer = customer; } private: QString m_customer; };
我们可以看到Order这个类中,用一个字符串来代表订单客户,但随着开发进度,很可能到了后期你需要为这个客户增加客户地址,信用等级等,这个时候你就不应该用字符串来表示客户,取而代之你应该使用对象。一开始使用Order这个类的客户端代码可能是这样
static int numberOfOrdersFor(QList<Order> orders, QString customer) { int result = 0; foreach (Order order, orders) { if (order.customer() == customer) { result++; } } return result; }
首先我们新建Customer类用来表示我们需要替换的新类,然后在这个新类中增加一个const字段用来表示源类中替换字段,这里表示客户姓名name这个概念,然后为这个字段增加取值函数和构造函数。
class Customer { public: Customer(const QString &name) : m_name(name) { } QString name() const { return m_name; } private: const QString m_name; };
接下来我们就需要修改源类了,首先我们将替换字段的类型从QString替换为我们所定义的类型Customer。然后修改所有引用该字段的函数,让他们改而去引用Customer这个对象,其中取值函数和构造函数的修改比较简单,设值函数我们让他重新创建一个新的Customer对象来跟之前的语义(字符串)达成一致形成值对象而非引用对象的概念。
class Order { public: Order(const QString &customer) { m_customer = new Customer(customer); } QString customer() const { return m_customer.name(); } void setCustomer(const QString &value) { delete m_customer; m_customer = NULL; m_customer = new Customer(value); } private: Customer m_customer; };
这也就意味着每个Order有属于自己的Customer,注意这样一条规则:值对象是不可以修改内容的。这便可以让你避免一些别名问题。如果你日后想让Customer成为引用对象(reference object)那就是另外一个重构手法了,现在我们进行编译并且测试。
同时需要观察Order类中m_customer字段的操作函数,并作出一些修改使它更好的反应修改后的形式。对于取值函数你可以使用Rename Method让它更清晰的表示自己,因为他返回的是消费者名称,因为此时的消费者已经是一个确确实实的类了。
QString customerName() const { return m_customer.name(); }
至于构造函数和设值函数,就不需要修改签名了,但你可以改变他们的参数名称。
Order(const QString &customerName) { m_customer = new Customer(customerName); } void setCustomer(const QString &customerName) { delete m_customer; m_customer = NULL; m_customer = new Customer(customerName); }
当然,后续的重构也许会添加接受现有的Customer对象作为参数的构造函数和设值函数。
本次重构到此为止,但这个案例和其他案例一样,只是一个中间步骤,还需要后期处理。因为从上文我们可以看到目前我们对待Customer这个类是采用值的概念来对待。如果要给这些Customer增加地址和信用等级我们做不到,因为多个Order存储的Customer不是共同引用同一个对象,所以我们必须使用Change Value to Reference,这样一来,同一个客户的所有Order就可以共享一个Customer对象达到我们所要的效果了。
『重构--改善既有代码的设计』读书笔记----Replace Data Value with Object
标签:
原文地址:http://www.cnblogs.com/rickyk/p/4177571.html