分享

重构笔记04】重新组织数据(2)

 昵称10504424 2014-01-03
重构笔记04】重新组织数据(2)

将单项关联变为双向关联

有时候,我们两个类需要使用对方属性,但期间只有一条单向连接
此时可以添加一个反向指针,并修改关系函数使他能够同时更新两条连接

开发初期,我们可能会在两个类之间建立一条单向连接,使其中一个类可以引用另一个类
随着时间推移,我们发现被引用类需要得到其引用者才能进行某些处理,也就是说需要一个反向指针
但是指针是一个单向连接,我们不能单向操作他,此时便可以建立双向引用关系了
PS:指针好像对js不是很实用的说

这个是我们以后会依赖的创建类的工具类

复制代码
 1 var base = {};
 2 var slice = [].slice;
 3 var bind = function (scope, fun, args) {
 4 args = args || [];
 5 return function () {
 6 fun.apply(scope, args.concat(slice.call(arguments)));
 7 };
 8 };
 9 
10 base.Class = function (supClass, childAttr) {
11 //若是第一个是类,便是继承;如果第一个是对象,第二个参数无意义,便是新建一个类
12 if (typeof supClass === 'object') {
13 childAttr = supClass;
14 supClass = function () { };
15 }
16 //新建临时类,最后作为新类返回,可能是继承可能是新类
17 /***
18 这里非常关键,为整个方法的入口,一定得看到初始化后,这里会执行构造函数
19 ***/
20 var newClass = function () {
21 //每个类都会使用该函数,作为第一步初始化,告诉类有哪些属性
22 this._propertys_ && this._propertys_();
23 //第二步初始化,相当于子类的构造函数,比较重要,初始化方法不一定会出现
24 this.init && this.init.apply(this, arguments);
25 };
26 //发生继承关系,可能为空类
27 newClass.prototype = new supClass();
28 
29 //新建类必定会包含初始化函数,要么继承,如果没继承,这里也会新建
30 var supInit = newClass.prototype.init || function () { };
31 //传入的子对象可能包含他的初始化方法,如果有一定要使用,至于父类使用与否看子类心情
32 var childInit = childAttr.init || function () { };
33 //父类的properys方法便是指定会具有哪些属性,一定会执行
34 var _supAttr = newClass.prototype._propertys_ || function () { };
35 //子类的初始化也一定会触发,先执行父类再执行子类
36 var _childAttr = childAttr._propertys_ || function () { };
37 
38 //为新建类(可能继承可能新建)初始化原型,上面的会重写,没有就不管他
39 for (var k in childAttr) {
40 childAttr.hasOwnProperty(k) && (newClass.prototype[k] = childAttr[k]);
41 }
42 
43 //处理继承情况
44 if (arguments.length && arguments[0].prototype && arguments[0].prototype.init === supInit) {
45 //根据父类重写新建类构造时会用到的方法
46 newClass.prototype.init = function () {
47 var scope = this;
48 var args = [function () {
49 //第一个参数为父类的初始化函数,执行与否看子类心情
50 supInit.apply(scope, arguments)
51 } ];
52 childInit.apply(scope, args.concat(slice.call(arguments)));
53 };
54 }
55 //前面说到的,父类与子类的初始化方法一定会执行,先父后子
56 newClass.prototype._propertys_ = function () {
57 _supAttr.call(this);
58 _childAttr.call(this);
59 };
60 
61 //成员属性也得继承
62 for (var k in supClass) {
63 supClass.hasOwnProperty(k) && (newClass[k] = supClass[k]);
64 }
65 return newClass;
66 };
复制代码

下面我们创建两个类,订单类Order与客户类Customer,其中Order引用了Customer,Customer没有引用Order

现在我们需要决定哪一个类负责控制关联关系,我们这里让单个类来操控,因为这样就可以将所有处理关联关系的逻辑安置到一地:

① 如果两者都是引用对象,而其间的关联为“一对多”关系,那么就由拥有单一引用的那一方承担控制者角色
本例中,一个客户可能拥有多个订单,就由Order承担控制者角色
② 如果某个对象是组成另一个对象的部件,那么由后者负责关联关系
③ 如果两者都是引用对象,其间是多对多关系,那么就随便了

本例中,由于Order负责关联关系,所以我们为Customer添加一个辅助函数,让Order可以直接访问订单集合
order的修改函数将使用这个辅助函数对指针两端对象进行同步控制
现在,我们改变函数时候,需要同时更新反向指针

PS:这段代码我看的也蛋疼,以下是这段蛋疼代码的蛋疼解释

类之间关系是各式各样的,因此修改函数的代码也会随之有所差异,如果_customer的值不是null,那么可以拿掉上述第一个null检查
但仍然需要检查传入参数是否为null,不过基本形式总是相同:先让对方删除指向你的指针,再将你的指针指向一个新对象,最后将那个新对象指针给自己
PS:这一段我没搞懂......
将双向关联改为单项关联与上述相反,我这里就不管他了,反正也看不懂

以字面常量取代魔法数

我们有一个字面常量,并且带有特别含义
那么创造一个常量,根据其意义为他命名,并将上述的字面数值替换为这个常量

这个比较简单,我们直接跳过了

封装集合

有个函数返回一个集合,让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合的函数

我们常常会在类中使用集合来保存一组实例,这样的类通常也会提供针对该集合的取值/设值函数
但是,集合的处理方式应该和其它种类数据略有不同,取值函数不该返回集合自身,因为这会让用户得以修改集合内容而拥有者一无所知
这样会对用户暴露过多的内部信息。
如果一个取值函数确实需要返回多个值,他应该避免用户之间操作对象内所保存的集合,并隐藏对象内存与用户无关的数据结构
另外,不应该为整个集合提供一个设值函数,但应该提供添加/移除函数,这样集合拥有者就可以控制集合元素的增删

怎么做

① 加入为集合添加/删除元素的函数
② 将保存集合的字段初始化为一个空集合
③ 找出集合设值函数的所有调用者,可以修改那个设值函数,让他使用新增的添加删除函数,也可以直接修改调用端,改用添加/删除函数
两种情况下需要用到集合设值函数:集合为空;准备将原有集合替换为另一集合
④ 找出所有通过取值函数获得集合并修改其内容的函数,逐一修改这些函数,将他们改用添加/删除函数
⑤ 修改完上述函数后,修改取值函数自身,使他返回集合的一个只读副本
⑥ 找出取值函数的所有用户,从中找出应该存于集合所属对象的代码,将这些代码移入宿主对象
⑦ 修改现有取值函数的名字,然后添加一个新取值函数,使其返回一个枚举,找出旧取值函数的所有被调用点改为新的

这里来一个例子吧:
假如有人要去上课,我们用一个简单的Course来表示课程
我们不关心课程细节,我们关心表示人的Person

有了这个我们可以为某人添加课程

现在,我们求高级课程的数量的代码就可以移入Person类了

结语

该篇文章对上一章进行说明

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多