重构手法之在对象之间搬移特性【4】
本小节目录
7Introduce Foreign Method(引入外加函数)
概要
你需要为提供服务的类增加一个函数,但你无法修改这个类。
在客户类中建立一个函数,并以第一参数形式传入一个服务类实例。
动机
好吧,我得不得说这个在C#中称为:扩展函数。这个其实也没什么好说的,这种事情发生过太多次了。假说你正在使用string类,它基本上提供了我们所需要的功能。但是,你正在做一项新服务,string类中恰巧无法提供。这时候你是不是想修改string的源代码,将这个功能加上。很可惜我们不能这么做,但是C#允许我们新建扩展函数。
范例
假如说,我们程序大量使用Dictionary<string,string>,我们要获得某个字典的value,可以这样做:
var dictionary = new Dictionary<string, string>(); string result; dictionary.TryGetValue("key", out result);
但是我们每次都得定义一个out参数,这样很不方便。于是乎,我们建立一个扩展函数,至于怎么新建扩展函数,请自行百度。
public static class DictionaryExt { public static string TryGetValue(this Dictionary<string, string> thisObj, string key) { if (thisObj == null || !thisObj.ContainsKey(key)) return null; return thisObj[key] ?? ""; } }
这样一来,我们在使用的时候,就可以这样:
var dictionary = new Dictionary<string, string>(); var res = dictionary.TryGetValue("key");
这样一来大大提高了我们的效率。
小结
如果客户类只是用这项功能一次,那么额外编码工作没什么大不了,甚至可能根本不需要原本提供服务的那个类。然而,如果你需要多次使用这个函数,就得不断重复这些代码。
重复代码是软件万恶之源,重复代码应该被抽出来放进同一个函数中。
8Introduce Local Extension(引入本地扩展)
概要
你需要为提供服务的类提供一些额外函数,但你无法修改这个类。
建立一个新类,使它包含这些额外函数。让这个扩展品成为源类的子类。
动机
类的作者无法预知未来,他们常常没能为你预先准备一些有用的函数。如果只需要一两个函数,可以使用Introduce Foreign Method。但是如果需要的额外函数超过两个,外加函数就很难控制它们了。所以将这些函数组织在一起,让其成为源类的子类。这个子类称为本地扩展。
范例
我们还是以Dictionary<string,string>这个为例。
首先新建一个DictionaryString,让其成为Dictionary<string,string>的子类。
class DictionaryString : Dictionary<string, string> { }
然后在扩展类中添加新特性,并使用Move Method将所有外加函数搬移到扩展类。
class DictionaryString : Dictionary<string, string> { public string TryGetValue(Dictionary<string, string> thisObj, string key) { if (thisObj == null || !thisObj.ContainsKey(key)) return null; return thisObj[key] ?? ""; } //other methods... }
使用方法和上个手法一样:
DictionaryString dic=new DictionaryString(); dic.TryGetValue("key");
小结
使用本地扩展让我们坚持了“函数和数据应该被统一封装”的原则。如果一直把本该放在扩展类中的代码零散地放置于其他类中,最终只会让其他这些类变得过分复杂,并使得其中函数难以被复用。
阶段性小结
在对象的设计过程中,“决定把责任放在哪儿”即使不是最重要的事,也是最重要的事之一。我们可能一开始并不能保证自己做对。但是我们可以使用Move Field和Move Method简单地移动对象行为,就可以解决这些问题。
类往往会因为承担过多责任而变得臃肿不堪。这时候可以使用Extract Class将一部分责任分离出去。如果一个类变得太“不负责任”,就使用Inline Class将它融入另一个类。如果一个类使用了另一个类,运用Hide Delegate将这种关系隐藏起来。有时候隐藏委托类会导致拥有者的接口经常变化,此时需要使用Remove Middle Man。
当不能访问某个类的源码,又想把责任移进这个不可修改的类时,要使用Introduce Foreign Method和Introduce Local Extension。如果加入的是一或两个函数,就会使用Introduce Foreign Method;如果不止一两个函数,就要使用Introduce Local Extension。
To Be Continued……