博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【重构笔记04】重新组织数据(2)
阅读量:6720 次
发布时间:2019-06-25

本文共 5715 字,大约阅读时间需要 19 分钟。

将单项关联变为双向关联

有时候,我们两个类需要使用对方属性,但期间只有一条单向连接

此时可以添加一个反向指针,并修改关系函数使他能够同时更新两条连接

开发初期,我们可能会在两个类之间建立一条单向连接,使其中一个类可以引用另一个类

随着时间推移,我们发现被引用类需要得到其引用者才能进行某些处理,也就是说需要一个反向指针
但是指针是一个单向连接,我们不能单向操作他,此时便可以建立双向引用关系了
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

var Order= base.Class({_propertys_: function () {this.customer = {};},getCustomer: function () {return this.customer;},setCustomer: function (arg) {this.customer = arg;}});var Customer = base.Class({_propertys_: function () {this.orders = {};}});

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

① 如果两者都是引用对象,而其间的关联为“一对多”关系,那么就由拥有单一引用的那一方承担控制者角色

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

本例中,由于Order负责关联关系,所以我们为Customer添加一个辅助函数,让Order可以直接访问订单集合

order的修改函数将使用这个辅助函数对指针两端对象进行同步控制
现在,我们改变函数时候,需要同时更新反向指针

var Order = base.Class({_propertys_: function () {this.customer = {};},getCustomer: function () {return this.customer;},setCustomer: function (arg) {if (this.customer != null) this.customer.friendOrders().remove(this);this.customer = arg;if (this.customer != null) this.customer.friendOrders().add(this);}});var Customer = base.Class({_propertys_: function () {this.orders = {};},friendOrders: function () {return this.orders;}});

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

类之间关系是各式各样的,因此修改函数的代码也会随之有所差异,如果_customer的值不是null,那么可以拿掉上述第一个null检查

但仍然需要检查传入参数是否为null,不过基本形式总是相同:先让对方删除指向你的指针,再将你的指针指向一个新对象,最后将那个新对象指针给自己
PS:这一段我没搞懂......
将双向关联改为单项关联与上述相反,我这里就不管他了,反正也看不懂

以字面常量取代魔法数

我们有一个字面常量,并且带有特别含义

那么创造一个常量,根据其意义为他命名,并将上述的字面数值替换为这个常量

1 function potentialEnergy(mass, height) {2 return mass * 9.81 * height;3 }4 var GRAVITATIONAL = 9.81;5 function potentialEnergy(mass, height) {6 return mass * GRAVITATIONAL * height;7 }

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

封装集合

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

我们常常会在类中使用集合来保存一组实例,这样的类通常也会提供针对该集合的取值/设值函数

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

怎么做

① 加入为集合添加/删除元素的函数

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

这里来一个例子吧:

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

var Course = base.Class({_propertys_: function () {this.customer = {};},init: function (name, isAdvanced) { },isAdvanced: function () { }});var Person = base.Class({_propertys_: function () {this.courses = {};},getCourses: function () {return this.courses;},setCourses: function (arg) {this.courses = arg;}});

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

var p = new Person();var s = [];s.push(new Course('金X瓶X梅', false));s.push(new Course('痴情关上观痴情', true));p.setCourses(s);//size 2console.log(p.getCourses());var refact = new Course('不再含苞待放的日子', true)p.getCourses().push(refact);p.getCourses().push(new Course('后庭花下会后庭', true));console.log(p.getCourses());// size 4p.getCourses().pop();console.log(p.getCourses());// size 3//如果想了解高级课程可以这样做:var count = 0for (var i = 0, len = p.getCourses().length; i < len; i++) { if (p.getCourses()[i].isAdvanced()) count++;}我们要做的第一件事就是为person中的集合添加合适的修改函数:var Person = base.Class({ _propertys_: function () { this.courses = []; }, getCourses: function () { return this.courses; }, initCourses: function (arg) { this.courses = arg; for (var i = 0, len = arg.length; i < len; i++) { this.addCourse(arg[i]); } }, addCourse: function (arg) { this.courses.push(arg); }, remove: function (arg) { //显然这个代码毫无意义,权当此处数组可以这样删除吧this.courses.remove(arg) }});

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

结语

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

本文转自叶小钗博客园博客,原文链接http://www.cnblogs.com/yexiaochai/p/3409379.html,如需转载请自行联系原作者

你可能感兴趣的文章
第十章
查看>>
C++typedefine用法小结
查看>>
CSS3边框特效
查看>>
Cocos2D坐标系
查看>>
获取word中的原始大小图片
查看>>
en_java去重排序
查看>>
(一)单例模式详解
查看>>
使用MAT分析内存泄露
查看>>
Android 原始套接字
查看>>
javascript 数组、json连接
查看>>
(寒假集训) 不等数列
查看>>
扫二维码登录实现原理,php版
查看>>
Foundation框架—— 数组 (NSArray NSMutableArray )
查看>>
UIView添加圆角边框
查看>>
简单用静态语言实现动态数组
查看>>
day-5 装饰器 迭代器 生成器
查看>>
Windows Bat脚本之变量延迟(Setlocal enabledelayedexpansion)
查看>>
word文档分别批量修改中文与英文字体大小字号等格式
查看>>
关于randbetween连乘的问题
查看>>
Vs程序自动获取windows7/vista系统管理员权限
查看>>