当我们调用第三方服务的类时,通常这个类我们都无法修改它,如果想要让他添加一个函数实现我们需要的功能这个时候可以使用Introduce Foreign Method引入外加函数, 但如果你需要的额外函数超过两个,外加函数(foreign methods)就很难控制住它 们了。所以,你需要将这些函数组织在一起,放到一个恰当地方去。要达到这一目 的,标准对象技术subclassing和wrapping是显而易见的办法。这种情况下我把subclass 或wrapper称为local extention(本地扩展〕
我将以Java 1.0.1的Date class为例。Java 1.1已经提供了我想要的功能,但是在它到来之前的那段日子,很多时候我需要扩展Java 1.0.1的Date class。第一件待决事项就是使用subclass或wrapper。subclassing是比较显而易见的办法:
Class mfDate extends Date {public nextDay()...public dayOfYear()...
wrapper则需要用上委托(delegation):
class mfDate {private Date _original;
首先,我要建立一个新的MfDateSub class来表示日期
class MfDateSub extends Date
然后,我需要处理Date 和我的extension class之间的不同处。MfDateSub 构造函数需要委托(delegating)给Date构造函数:
public MfDateSub (String dateString) {super (dateString);};
现在,我需要加入一个转型构造函数,其参数是一个隶属原类的对象:
public MfDateSub (Date arg) {super (arg.getTime());}
现在,我可以在extension class中添加新特性,并使用Move Method 将所有外加函数(foreign methods)搬移到extension class。于是,下面的代码:
client class...private static Date nextDay(Date arg) {// foreign method, should be on datereturn new Date (arg.getYear(),arg.getMonth(), arg.getDate() + 1);}
经过搬移之后,就成了:
class MfDate...Date nextDay() {return new Date (getYear(),getMonth(), getDate() + 1);}
首先声明一个wrapping class:
class mfDate {private Date _original;}
使用wrapping方案时,我对构造函数的设定与先前有所不同。现在的构造函数将只是执行一个单纯的委托动作(delegation):
public MfDateWrap (String dateString) {_original = new Date(dateString);};
而转型构造函数则只是对其instance变量赋值而己:
public MfDateWrap (Date arg) {_original = arg;}
接下来是一项枯燥乏味的工作:为原始类的所有函数提供委托函数。我只展示两个函数,其他函数的处理依此类推。
public int getYear() {return _original.getYear();}public boolean equals (MfDateWrap arg) {return (toDate().equals(arg.toDate()));}
完成这项工作之后,我就可以后使用Move Method 将日期相关行为搬移到新class中。于是以下代码:
client class...private static Date nextDay(Date arg) {// foreign method, should be on datereturn new Date (arg.getYear(),arg.getMonth(), arg.getDate() + 1);}
经过搬移之后,就变成:
class MfDate...Date nextDay() {return new Date (getYear(),getMonth(), getDate() + 1);}
使用wrappers有一个特殊问题:如何处理「接受原始类之实体为参数」的函数?例如:public boolean after (Date arg)由于无法改变原始类〔original),所以我只能以一种方式使用上述的after() :
aWrapper.after(aDate) // can be made to workaWrapper.after(anotherWrapper) // can be made to workaDate.after(aWrapper) // will not work
这样覆写(overridden)的目的是为了向用户隐藏wrapper 的存在。这是一个好策略,因为wrapper 的用户的确不应该关心wrapper 的存在,的确应该可以同样地对待wrapper(外覆类)和orignal((原始类)。但是我无法完全隐藏此一信息,因为某些系统所提供的函数(例如equals() 会出问题。你可能会认为:你可以在MfDateWrap class 中覆写equals(),像这样:
public boolean equals (Date arg) // causes problems
但这样做是危险的,因为尽管我达到了自己的目的,Java 系统的其他部分都认为equals() 符合交换律:如果a.equals(b)为真,那么b.equals(a)也必为真。违反这一规则将使我遭遇一大堆莫名其妙的错误。要避免这样的尴尬境地,惟一办法就是修改Date class。但如果我能够修改Date ,我又何必进行此项重构?所以,在这种情况下,我只能(必须〕向用户暴露「我进行了包装」这一事实。我将以一个新函数来进行日期之间的相等性检查(equality tests):
public boolean equalsDate (Date arg)
我可以重载equalsDate() ,让一个重载版本接受Date 对象,另一个重载版本接受MfDateWrap 对象。这样我就不必检查未知对象的型别了:
public boolean equalsDate (MfDateWrap arg)
subclassing方案中就没有这样的问题,只要我不覆写原函数就行了。但如果我覆写了original class 中的函数,那么寻找函数时,我会被搞得晕头转向。一般来说,我不会在extension class 中覆写0original class 的函数,我只会添加新函数。