Introduce Null Object(引入Null对象)
2009-02-19 17:14
323 查看
需要再三检查某物是否为null value。
将null value (无效值)替换为 null object(无效对象)。
if (customer == null) plan = BillingPlan.basic();
else plan = customer.getPlan();
==〉
动机
多态的最根本好处是不必再向对象询问你是什么类型,而后根据答案调用对象的某个行为,你只管调用该行为就是了,其他的多态机制会负责。
当某个值域内容是null value 多态可扮演另一个较不直观的用途。
提供一个虚构的对象,这样就不用每次检查对象是否存在。在现实信息的时候使用null object。因为null object和实体的变量是null相比知道如何显示。
还可以使用null object保存不存在的session。然后过一段时间后在将存放在session中的数据一次更新到数据库中。
null object的另一个用途是表现出虚构的箱仓(missing bin)。
null object对所有的外界请求都响应,所以系统的行为都是正确的。
使用null object必须是常量,它的任何成分都不会发生变化。因此我们可以使用Singleton模式 来实现它们。
关于Null Object模式可以在woolf中找到更多的详细介绍。
作法
1. 为源类建立一个子类,使其行为象源类的null版本。在源类和null类中都加上IsNull()函数,前者的应该返回false,后者的返回true。
建议一个nullable接口,将IsNull()函数放在其中,让源类实现这个接口。
你也可以创建一个testing接口,专门用来检查对象是否为null。
2. 编译。
3. 找出所有索求源对象却获得一个null的地方。修改这些地方,使它们改而获得一个null object。
4. 找出所有将源对象与null做比较的地方。修改这些地方,使它们调用IsNull()函数。
每次处理一个源对象及客户程序。
在不该出现null的地方放上一些断言。
5. 编译,测试。
6. 找出这样的程序点,如果对象不是null做A,否则做B.
7. 对于上述地点,在null class中覆写A动作,使其行为和B相同。
8. 使用上述被覆写的动作A,然后删除对象是否等于null的条件测试。编译并测试。
一家公用事业公司的系统以site表示地点(场所)、庭院宅第(hose)和集合公寓(apartment)都使用该公司的服务。任何时候每个地点都拥有一个顾客,顾客信息以Custom表示:
class Site...
Customer getCustomer() {
return _customer;
}
Customer _customer;
Custom有很多特性,我们只看其中三项
class Customer...
public String getName() {...}
public BillingPlan getPlan() {...}
public PaymentHistory getHistory() {...}
本系统又以PaymentHistory表示顾客的付款纪录,它也有它自己的特性:
public class PaymentHistory...
int getWeeksDelinquentInLastYear()
上面的各种取值函数允许客户取得各种数据。但有时候一个地点的顾客搬走了。新顾客还没搬进来,此时这个地点就没有顾客。由于这种情况有可能发生,所以我必须保证Custom的所有用户能够处理Custom等于null的情况。下面是一些示例片断
Customer customer = site.getCustomer();
BillingPlan plan;
if (customer == null) plan = BillingPlan.basic();
else plan = customer.getPlan();
...
String customerName;
if (customer == null) customerName = "occupant";
else customerName = customer.getName();
...
int weeksDelinquent;
if (customer == null) weeksDelinquent = 0;
else weeksDelinquent =
customer.getHistory().getWeeksDelinquentInLastYear();
这个系统中可能使用site和Custom,它们都必须检查Custom对象是否等于null,而这样的检查完全是重复的。看来使用null object的时候了。
首先,新建一个null customer,并修改Customer,使其支持对象是否为null的检查
class NullCustomer extends Customer {
public boolean isNull() {
return true;
}
}
class Customer...
public boolean isNull() {
return false;
}
protected Customer() {} //needed by the NullCustomer
如果你无法修改Customer,你可以建立一个新的testing接口。
如果你喜欢,也可以新建一个接口,告诉大家,这里使用了null object
interface Nullable {
boolean isNull();
}
class Customer implements Nullable
我还喜欢加入一个factory method,专门用来创建NullCustomer对象,这样一来,用户就不必知道null class的存在了。
class Customer...
static Customer newNull() {
return new NullCustomer();
}
接下来的部分稍微有点复杂,对于所有返回null的地方,我都要将它返回null object。此时,我还要把foo == null的情况,改为foo.isNull()的情况。我发现下列方法很有用,查找所有索求Customer对象的地方,将它们都加以修改,使它们不能返回null,改而返回一个NullCustomer对象
class Site...
Customer getCustomer() {
return (_customer == null) ?
Customer.newNull():
_customer;
}
另外,我还需要修改所有使用Customer的地方,让它们用isNull的函数检查,而不要使用==null的检查方式
Customer customer = site.getCustomer();
BillingPlan plan;
if (customer.isNull()) plan = BillingPlan.basic();
else plan = customer.getPlan();
...
String customerName;
if (customer.isNull()) customerName = "occupant";
else customerName = customer.getName();
...
int weeksDelinquent;
if (customer.isNull()) weeksDelinquent = 0;
else weeksDelinquent =
customer.getHistory().getWeeksDelinquentInLastYear();
毫无疑问,这是本项重构中最需要技巧的地方,对于每个需要替换的可能等于null的对象,我都必须找到它是否等于null的所有检查工作,并逐一替换。
这个步骤完成之后,如果编译和测试都顺利通过,我就可以宽心的露出笑容了。接下来的动作比较有趣,到目前为止,使用isnull函数尚未带来任何好处。只有等我把相关行为移到null customer对象中并去除条件式之后,我才能得到切实的利益。我可以逐一将各种行为(函数)移过去,首先从取得顾客名称这个函数开始,此时客户端的代码大约如下:
String customerName;
if (customer.isNull()) customerName = "occupant";
else customerName = customer.getName();
首先,为null customer加入一个合适的函数,通过这个函数来取得顾客名称
class NullCustomer...
public String getName(){
return "occupant";
现在,我可以去掉条件代码了,
String customerName = customer.getName();
接下来,我以相同手法处理其他函数,使它们对相应查询做出合适的响应,此外我还可以对修改函数做适当的处理,于是下面这样的客户端程序
if (! customer.isNull())
customer.setPlan(BillingPlan.special());
就变成了这样
customer.setPlan(BillingPlan.special());
class NullCustomer...
public void setPlan (BillingPlan arg) {}
请记住,只有当大多数代码都要求null object做出响应时,这样的行为搬移才有意义。注意,我说的是大多数而不是所有,任何用户,如果需要null object作出不同响应,他仍然可以使用isNull来测试,只要大多数客户端,都要求nul object做出相同响应,他们就可以调用缺省的null行为,而你也就受益匪浅了。
上述范例略带差异的情况是,某些客户端使用Custom函数的运算结果:
if (customer.isNull()) weeksDelinquent = 0;
else weeksDelinquent =
customer.getHistory().getWeeksDelinquentInLastYear();
我们新建一个NullPaymentHistory类,用以处理这种情况
class NullPaymentHistory extends PaymentHistory...
int getWeeksDelinquentInLastYear() {
return 0;
}
并修改NullCustomer 让它返回一个NullPaymentHistory
class NullCustomer...
public PaymentHistory getHistory() {
return PaymentHistory.newNull();
}
然后,我同样可以删除一行条件代码
int weeksDelinquent =
customer.getHistory().getWeeksDelinquentInLastYear();
你常常可以看到这样的情况,null objects会返回其他的null objects。
将null value (无效值)替换为 null object(无效对象)。
if (customer == null) plan = BillingPlan.basic();
else plan = customer.getPlan();
==〉
动机
多态的最根本好处是不必再向对象询问你是什么类型,而后根据答案调用对象的某个行为,你只管调用该行为就是了,其他的多态机制会负责。
当某个值域内容是null value 多态可扮演另一个较不直观的用途。
提供一个虚构的对象,这样就不用每次检查对象是否存在。在现实信息的时候使用null object。因为null object和实体的变量是null相比知道如何显示。
还可以使用null object保存不存在的session。然后过一段时间后在将存放在session中的数据一次更新到数据库中。
null object的另一个用途是表现出虚构的箱仓(missing bin)。
null object对所有的外界请求都响应,所以系统的行为都是正确的。
使用null object必须是常量,它的任何成分都不会发生变化。因此我们可以使用Singleton模式 来实现它们。
关于Null Object模式可以在woolf中找到更多的详细介绍。
作法
1. 为源类建立一个子类,使其行为象源类的null版本。在源类和null类中都加上IsNull()函数,前者的应该返回false,后者的返回true。
建议一个nullable接口,将IsNull()函数放在其中,让源类实现这个接口。
你也可以创建一个testing接口,专门用来检查对象是否为null。
2. 编译。
3. 找出所有索求源对象却获得一个null的地方。修改这些地方,使它们改而获得一个null object。
4. 找出所有将源对象与null做比较的地方。修改这些地方,使它们调用IsNull()函数。
每次处理一个源对象及客户程序。
在不该出现null的地方放上一些断言。
5. 编译,测试。
6. 找出这样的程序点,如果对象不是null做A,否则做B.
7. 对于上述地点,在null class中覆写A动作,使其行为和B相同。
8. 使用上述被覆写的动作A,然后删除对象是否等于null的条件测试。编译并测试。
一家公用事业公司的系统以site表示地点(场所)、庭院宅第(hose)和集合公寓(apartment)都使用该公司的服务。任何时候每个地点都拥有一个顾客,顾客信息以Custom表示:
class Site...
Customer getCustomer() {
return _customer;
}
Customer _customer;
Custom有很多特性,我们只看其中三项
class Customer...
public String getName() {...}
public BillingPlan getPlan() {...}
public PaymentHistory getHistory() {...}
本系统又以PaymentHistory表示顾客的付款纪录,它也有它自己的特性:
public class PaymentHistory...
int getWeeksDelinquentInLastYear()
上面的各种取值函数允许客户取得各种数据。但有时候一个地点的顾客搬走了。新顾客还没搬进来,此时这个地点就没有顾客。由于这种情况有可能发生,所以我必须保证Custom的所有用户能够处理Custom等于null的情况。下面是一些示例片断
Customer customer = site.getCustomer();
BillingPlan plan;
if (customer == null) plan = BillingPlan.basic();
else plan = customer.getPlan();
...
String customerName;
if (customer == null) customerName = "occupant";
else customerName = customer.getName();
...
int weeksDelinquent;
if (customer == null) weeksDelinquent = 0;
else weeksDelinquent =
customer.getHistory().getWeeksDelinquentInLastYear();
这个系统中可能使用site和Custom,它们都必须检查Custom对象是否等于null,而这样的检查完全是重复的。看来使用null object的时候了。
首先,新建一个null customer,并修改Customer,使其支持对象是否为null的检查
class NullCustomer extends Customer {
public boolean isNull() {
return true;
}
}
class Customer...
public boolean isNull() {
return false;
}
protected Customer() {} //needed by the NullCustomer
如果你无法修改Customer,你可以建立一个新的testing接口。
如果你喜欢,也可以新建一个接口,告诉大家,这里使用了null object
interface Nullable {
boolean isNull();
}
class Customer implements Nullable
我还喜欢加入一个factory method,专门用来创建NullCustomer对象,这样一来,用户就不必知道null class的存在了。
class Customer...
static Customer newNull() {
return new NullCustomer();
}
接下来的部分稍微有点复杂,对于所有返回null的地方,我都要将它返回null object。此时,我还要把foo == null的情况,改为foo.isNull()的情况。我发现下列方法很有用,查找所有索求Customer对象的地方,将它们都加以修改,使它们不能返回null,改而返回一个NullCustomer对象
class Site...
Customer getCustomer() {
return (_customer == null) ?
Customer.newNull():
_customer;
}
另外,我还需要修改所有使用Customer的地方,让它们用isNull的函数检查,而不要使用==null的检查方式
Customer customer = site.getCustomer();
BillingPlan plan;
if (customer.isNull()) plan = BillingPlan.basic();
else plan = customer.getPlan();
...
String customerName;
if (customer.isNull()) customerName = "occupant";
else customerName = customer.getName();
...
int weeksDelinquent;
if (customer.isNull()) weeksDelinquent = 0;
else weeksDelinquent =
customer.getHistory().getWeeksDelinquentInLastYear();
毫无疑问,这是本项重构中最需要技巧的地方,对于每个需要替换的可能等于null的对象,我都必须找到它是否等于null的所有检查工作,并逐一替换。
这个步骤完成之后,如果编译和测试都顺利通过,我就可以宽心的露出笑容了。接下来的动作比较有趣,到目前为止,使用isnull函数尚未带来任何好处。只有等我把相关行为移到null customer对象中并去除条件式之后,我才能得到切实的利益。我可以逐一将各种行为(函数)移过去,首先从取得顾客名称这个函数开始,此时客户端的代码大约如下:
String customerName;
if (customer.isNull()) customerName = "occupant";
else customerName = customer.getName();
首先,为null customer加入一个合适的函数,通过这个函数来取得顾客名称
class NullCustomer...
public String getName(){
return "occupant";
现在,我可以去掉条件代码了,
String customerName = customer.getName();
接下来,我以相同手法处理其他函数,使它们对相应查询做出合适的响应,此外我还可以对修改函数做适当的处理,于是下面这样的客户端程序
if (! customer.isNull())
customer.setPlan(BillingPlan.special());
就变成了这样
customer.setPlan(BillingPlan.special());
class NullCustomer...
public void setPlan (BillingPlan arg) {}
请记住,只有当大多数代码都要求null object做出响应时,这样的行为搬移才有意义。注意,我说的是大多数而不是所有,任何用户,如果需要null object作出不同响应,他仍然可以使用isNull来测试,只要大多数客户端,都要求nul object做出相同响应,他们就可以调用缺省的null行为,而你也就受益匪浅了。
上述范例略带差异的情况是,某些客户端使用Custom函数的运算结果:
if (customer.isNull()) weeksDelinquent = 0;
else weeksDelinquent =
customer.getHistory().getWeeksDelinquentInLastYear();
我们新建一个NullPaymentHistory类,用以处理这种情况
class NullPaymentHistory extends PaymentHistory...
int getWeeksDelinquentInLastYear() {
return 0;
}
并修改NullCustomer 让它返回一个NullPaymentHistory
class NullCustomer...
public PaymentHistory getHistory() {
return PaymentHistory.newNull();
}
然后,我同样可以删除一行条件代码
int weeksDelinquent =
customer.getHistory().getWeeksDelinquentInLastYear();
你常常可以看到这样的情况,null objects会返回其他的null objects。
相关文章推荐
- Introduce Parameter Object(引入参数对象)
- Introduce Parameter Object (引入参数对象)
- Introduce Null Object(引入Null对象)
- 重构手法40:Introduce Null Object (引入Null 对象)
- Introduce Null Object (引入Null对象)
- 9.7 introduce Null object (引入null对象)
- 重构指南 - 引入参数对象(Introduce Parameter Object)
- 重构手法50:Introduce Parameter Object (引入参数对象)
- 简化函数调用之九 :Introduce Parameter Object(引入参数对象)
- 在对象之间搬移特性之七 :Introduce Foreign Method(引入外加函数)
- jdbcTemplet.queryforobject 如果返回为null,或者多个对象。会报异常!!!!!
- 在对象之间搬移特性之八 :Introduce Local Extension(引入本地扩展)
- 【JS】null是用来表示对象的(null is as a placeholder for an object)
- 重构:引入NULL对象
- 调用对象属性(或方法)保证健壮性的一个小技巧——使Object != null
- javascript数组、对象和Null的typeof同为object,区分解决办法
- TypeError: 'bases' is null or not an object。IE8 bug 腐朽的对象
- 设计模式之空对象模式--- Pattern Null Object
- 4.7 引入NULL对象
- 设计模式【空对象模式NullObjectPattern】