《重构:改善既有代码的设计》中提到过很多重构方法,关于简化条件表达式的方法有8种。本文介绍:
引入null对象 introduce null object
- 名称:引入null对象 introduce null object
- 概要:检查某对象是否为null, 将null值替换为null对象
- 动机: 更好的使用多态,而不必关心这是什么类型
- 做法:
- 为源类建立一个子类,使其行为就像时源类的null 版本。在源类和null子类中加上isNull()函数
- 编译
- 找出所有“索求源对象却获得一个null”的地方,修改这些地方,使他们改而获取一个空对象
- 找出所有“将源对象与null做比较”的地方。修改这些地方,使它们调用isNull()函数。在“不该再出现null”的地方放上一些断言,确保null的确不再出现。
- 编译,测试
- 找出这样的程序点:如果对象不是null,做A动作,否则做B动作
- 对于每一个上述地点,在null类中覆写A动作,使其行为和B动作相同
- 使用上述被覆写的动作,然后删除“对象是否等于null”的条件测试。编译并测试。
- 代码演示
- 一家共用事业公司的系统以site表示地点,house和 apartment都使用该公司的服务。任何时候每个地点都拥有一个顾客。如果一个地点的顾客搬走了,新顾客还没有搬进来,此时这个地点就没有顾客。所以,必须保证customer的所有用户都能够处理“customer对象等于null”的情况。
修改之前的代码:
///.h
class BillingPlan
{
public:
static BillingPlan * basic();
};
class PaymentHistory
{
public:
int getWeeksDelingquentInLastYear();
};
class Customer
{
public:
QString getName() const;
BillingPlan* getPlan() const;
PaymentHistory getHistory() const;
private:
QString m_Name;
BillingPlan *m_Plan;
PaymentHistory m_History;
};
class Site
{
public:
Customer *getCustomer() const;
private:
Customer * m_customer;
};
///.cpp
Customer *Site::getCustomer() const
{
return m_customer;
}
QString Customer::getName() const
{
return m_Name;
}
BillingPlan* Customer::getPlan() const
{
return m_Plan;
}
PaymentHistory Customer::getHistory() const
{
return m_History;
}
int PaymentHistory::getWeeksDelingquentInLastYear()
{
return 10;
}
BillingPlan *BillingPlan::basic()
{
return new BillingPlan();
}
/main.cpp
Site site;
Customer *customer = site.getCustomer();
BillingPlan *plan;
if (customer == nullptr)
plan = BillingPlan::basic();
else
plan = customer->getPlan();
QString customerName;
if (customer == nullptr)
customerName = "occupant";
else
customerName = customer->getName();
int weeksDelinquent;
if (customer == nullptr)
weeksDelinquent = 0;
else
weeksDelinquent = customer->getHistory().getWeeksDelingquentInLastYear();
1)这个系统中可能有许多地方使用site和customer对象,它们都必须检查customer对象是否为nullptr,而这样的检查完全使重复的。
2)新建一个NullCustomer,并修改customer,使其支持“对象是否为null”的检查
3)加入一个工厂函数,专门用来创建NullCustomer对象。这样一来,用户就不必知道空对象的存在了。
4)对于所有“返回null”的地方,我都要将它改为“返回空对象”。
5) 设置 nullcustomer的getname()
修改之后的代码:
///.h
class BillingPlan
{
public:
static BillingPlan * basic();
};
class PaymentHistory
{
public:
int getWeeksDelingquentInLastYear();
};
class Customer
{
public:
QString getName() const;
BillingPlan* getPlan() const;
PaymentHistory getHistory() const;
bool isNull();
static Customer * newNull();
protected:
Customer();
private:
QString m_Name;
BillingPlan *m_Plan;
PaymentHistory m_History;
};
class NullCustomer : public Customer
{
public:
QString getName() const;
bool isNull();
};
class Site
{
public:
Customer *getCustomer() const;
private:
Customer * m_customer;
};
///.cpp
Customer *Site::getCustomer() const
{
return (m_customer == nullptr) ? Customer::newNull() : m_customer;
}
QString Customer::getName() const
{
return m_Name;
}
BillingPlan* Customer::getPlan() const
{
return m_Plan;
}
PaymentHistory Customer::getHistory() const
{
return m_History;
}
bool Customer::isNull()
{
return false;
}
Customer *Customer::newNull()
{
return new NullCustomer();
}
Customer::Customer()
{
//needed by the NullCustomer
}
int PaymentHistory::getWeeksDelingquentInLastYear()
{
return 10;
}
BillingPlan *BillingPlan::basic()
{
return new BillingPlan();
}
QString NullCustomer::getName() const
{
return "occupant";
}
bool NullCustomer::isNull()
{
return true;
}
///main.cpp
Site site;
Customer *customer = site.getCustomer();
BillingPlan *plan;
if (customer->isNull())
plan = BillingPlan::basic();
else
plan = customer->getPlan();
QString customerName = customer->getName();
// if (customer->isNull())
// customerName = "occupant";
// else
// customerName = customer->getName();
int weeksDelinquent;
if (customer->isNull())
weeksDelinquent = 0;
else
weeksDelinquent = customer->getHistory().getWeeksDelingquentInLastYear();
对于BillingPlan和PaymentHistory类,可以以设置null对象