前言
在面向对象设计过程中,“决定把责任放在那儿”即使不是最重要的事,也是最重要的事之一。
这个思想其实对类较重要,在js中,函数应该完成什么职责,也需要分清楚,不要函数做了自己的事情,又做了一部分其它函数的事情。
在js中,往往只有一个类,面向对象的思想其实比较难,没有一定功力不能很好的运用,我也在学习
很多js中其实就只有一个类,一个插件就是一个类,而且这个类还是假的,继承、封装神马的要理解还是不容易的,所以路还长!
搬迁函数
我们的程序中如果有个函数与其所驻类的另一个类有更多的交流(调用后者、或者被后者调用)
那么在该函数最常引用的类中建立一个有着类似行为的新函数,将久函数变成一个单纯的委托函数,或者移除旧函数
搬迁函数是重构理论的支柱,如果一个类有太多行为,或者一个类与另一个类有太多合作而成高度耦合,我们就需要搬迁函数
PS:其实在单页应用中,MVC的结构很可能导致这种情况发现,后面点我们搞点代码出来看看
我们的程序中会有这样的函数,使用另一个对象次数比使用自己的次数还多,搬迁时这就是函数归属的重要依据
来一个例子吧,这里利用一个表示“账户”的Account类来说明
1 var AccountType = function () {};
2 AccountType.prototype = {
3 isPremium: function () { return true; }
4 };
5 var Account = function () {
6 this._type = new AccountType();
7 this._daysOverdran;
8 };
9 Account.prototype = {
10 overdraftCharge: function () {
11 if (this._type.isPremium()) {
12 var result = 10;
13 if (this._daysOverdran > 7) result += (this._daysOverdrawn - 7) * 0.85;
14 return result;
15 } else {
16 return this._daysOverdran * 1.75;
17 }
18 },
19 bankCharge: function () {
20 var result = 4.5;
21 if (this._daysOverdran > 0) result += this.overdraftCharge();
22 return result;
23 }
24 };
在这个程序中,加入有几种新账户,每一种都有自己的“透支金额计费规则”,所以overdraftCharge可能需要搬迁到AccountType中去
如何做
首先,我们要观察overdraftCharge使用的每一项特性,考虑那些特性是否可与他一起搬迁
这个程序中,_dayOverdrawn会被留下,因为这个值会随不同账号变化而变化,于是调整后的代码如下:
1 var AccountType = function () {};
2 AccountType.prototype = {
3 isPremium: function () { return true; },
4 overdraftCharge: function (account) {
5 if (this.isPremium()) {
6 var result = 10;
7 if (account.getDaysOverdran() > 7) result += (account.getDaysOverdran() - 7) * 0.85;
8 return result;
9 } else {
10 return account.getDaysOverdran() * 1.75;
11 }
12 }
13 };
14 var Account = function () {
15 this._type = new AccountType();
16 this._daysOverdran;
17 };
18 Account.prototype = {
19 bankCharge: function () {
20 var result = 4.5;
21 if (this._daysOverdran > 0) result += this._type.overdraftCharge(this);
22 return result;
23 }
24 };
这里也可以将daysOverdrawn作为参数传入,但是如果后面会传入多个字段,就要改代码,所以直接传入对象吧
搬移字段
搬移字段在再js程序中可能用得会多一点,在我们的程序中,某个字段被自己类之外的类多次使用的话
那么,在目标类新建一个字段,修改源字段的所有用户,让他们使用新字段
为什么这么干?
在类之间移动状态和行为,是重构过程中必不可少的措施,随着系统发展,我们会发现,自己需要新的类,并需要将现有工作责任移动新类中
但是这个星期看似合理正确的设计决策,下个星期可能就错了的情况也不是没有
如果我们发现,一个字段被其它类过多使用,那么我们就该抛弃他了
范例
继续我们上面的例子吧
1 var Account = function () {
2 this._type = new AccountType();
3 this._interestRate;
4 };
5 Account.prototype = {
6 interestForAmount_days: function (amount, days) {
7 return this._interestRate * amount * days / 365;
8 }
9 };
我们现在想把表示利率的_interestRate搬移到AccountType类去,目前已经有几个函数引用他了,interestForAmount_days是其中之一
所以我们现在AccountType中建立_interestRate字段与相应函数,以下是重构后的代码
1 var AccountType = function () {
2 this._interestRate;
3 };
4 AccountType.prototype = {
5 getInterestRate: function () {
6 return this._interestRate;
7 }
8 };
9 var Account = function () {
10 this._type = new AccountType();
11 };
12 Account.prototype = {
13 interestForAmount_days: function (amount, days) {
14 return this.type.getInterestRate() * amount * days / 365;
15 }
16 };
提炼类
某个类做了应该两个类做的事,那么就新建一个类,将相关的字段和函数移过去
PS:我们从开始到现在就了解了,重构的一大手段就是消除临时变量,另一个方法就是职责分离了
我们经常听到这句话,一个类应该是一个清除的抽象,处理一些明确的责任,但在实际工作中,类会不断扩张
我们会在这加一些功能,然后在那加一些数据,给某个类添加一项新任务时,你会觉得不值得为这项责任分离出一个单独的类
于是责任不断增加,这个类最后就会越来越复杂,很快这个类就连自己也不想读了
这样的类往往有大量的函数和数据,所以不好理解,这个时候我们需要考虑应该分离哪部分,并将它们写到一个单独的类中
如果这些数据和某些函数总是一起出现,某些数据经常同时变化,那么就分离吧
PS:说了这么多还是虚的,来一段代码吧
1 var Person = function () {
2 this._name;
3 this._officeAreaCode;
4 this._officeNumber;
5 };
6 Person.prototype = {
7 getName: function () { return this._name; },
8 getTel: function () {
9 return '(' + this._officeAreaCode + ')' + this._officeNumberl;
10 },
11 getOfficeAreaCode: function () {
12 return this._officeAreaCode;
13 },
14 setOfficeAreaCode: function (arg) {
15 this._officeAreaCode = arg;
16 },
17 getOfficeNumber: function () {
18 return this._officeNumber;
19 },
20 setOfficeNumber: function (arg) {
21 this._officeNumber = arg;
22 }
23 };
这里,我们可以将电话号码相关行为分离到一个独立的类中,优化后的代码:
1 var Tel = function () {
2 this._number;
3 this._areaCode;
4 };
5 Tel.prototype = {
6 getAreaCode: function () {
7 return this._areaCode;
8 },
9 setAreaCode: function (arg) {
10 this._areaCode = arg;
11 },
12 getNumber: function () {
13 return this._number;
14 },
15 setNumber: function (arg) {
16 this._number = arg;
17 }
18 };
19 var Person = function () {
20 this._name;
21 this.tel = new Tel();
22 };
23 Person.prototype = {
24 getName: function () { return this._name; },
25 getTel: function () {
26 return '(' + this.tel.getAreaCode() + ')' + this.tel.getNumber();
27 }
28 };
想想,Person当然不只Tel这点属性,Tel也可能会有其它动作,如果写在一起,后面代码膨胀是可以预见的
将类内联化
如果某个类没做什么事情,那么将类消除吧,将他完成的特性移动到另一个类中,这个方法与提炼类完全相反
范例就是将上面的代码写回去......
隐藏委托关系
客户通过一个委托来调用另一个对象,在服务器上建立客户的所有函数,用以隐藏委托关系
“封装”即使不是对象最关键的特征,也是最关键的特征之一
封装意味着每个对象都应该尽可能少了解系统其它部分,如此一来,其它地方变化了,就管我屁事了(变化比较容易进行)
范例
本例中,出了人以为,多了一个部门的类啦:
1 var Department = function (manager) {
2 this._chargeCode;
3 this._manager = manager;
4 };
5 Department.prototype = {
6 getManager: function () {
7 return this._manager;
8 }
9 };
10 var Person = function () {
11 this._department;
12 };
13 Person.prototype = {
14 getDepartment: function () {
15 return this._department;
16 }
17 };
如果客户想知道某人的经理是谁,他必须先取得Department对象
manager = p.getDepartment().getManager()
这样有一个问题就是,对用户揭露了Department的工作原理,于是客户指定通过Department可以追踪经理信息,如果可以隐藏Department的话,就可以减少耦合
这里就来一个委托:
1 var Department = function (manager) {
2 this._chargeCode;
3 this._manager = manager;
4 };
5 Department.prototype = {
6 getManager: function () {
7 return this._manager;
8 }
9 };
10 var Person = function () {
11 this._department;
12 };
13 Person.prototype = {
14 getDepartment: function () {
15 return this._department;
16 },
17 getManager: function () {
18 return this._department.getManager();
19 }
20 };
这样的话,用户就不知道有Department这个东西了
移除中间人
某个类做了过多的简单委托动作,那么就让用户之间调用委托类吧
这个与上面的是反着的,各位对照着看吧
引入外加函数
我们有时需要为提供服务的类增加一个函数,但我们无法修改这个类,那么在客户类中建立一个函数,并以第一参数形式传入一个服务器类实例
1 var d = new Date(previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDay() + 1);
2
3 var d = nextDay(previousEnd);
4 function nextDay(arg) {
5 return new Date(arg.getYear(), arg.getMonth(), arg.getDay() + 1);
6 }
这种事情发生的很多,我们正在使用一个类,他提供了很多服务,但是我们突然需要一个新类,这个类却无法提供,那么我们可能会咒骂,完了修改源码
如果源码不能修改,我们就得在客户端补齐函数啦
引入本地扩展
你需要为服务类提供一些额外函数,但你无法修改这个类,建立一个新类,使他包含这些额外的函数,让这个扩展成为源类的子类或者包装类
这个与上面类似,小的要看神都龙王去了,所以暂时到这吧......
本文转自叶小钗博客园博客,原文链接http://www.cnblogs.com/yexiaochai/p/3378915.html,如需转载请自行联系原作者