直播开始:'云榨汁机'诞生记--聊聊JavaScript中的'业务建模'
Posted 阿来之家
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了直播开始:'云榨汁机'诞生记--聊聊JavaScript中的'业务建模'相关的知识,希望对你有一定的参考价值。
闭包是javascript中的一个重要特性,在之前的博文中,我们说闭包是一个\'看似简单,其实很有内涵\'的特性。当我们用JavaScript来实现相对复杂的业务建模时,我们可以如何利用\'闭包\'这个特性呢?JavaScript中的\'原型继承\',又可以解决业务建模中的哪些问题呢?今天我们就通过一家\'榨汁机工厂\'生产设计\'榨汁机\'的故事,来聊一聊\'闭包\'和\'原型继承\'在业务建模中的作用。
现在直播开始:
1》 工厂默认选用A型刀头方案制造榨汁机
例子当中我们主要涉及到2个函数:
1.榨汁机的生产工厂(JuiceMachineFactory)。
2.生产榨汁机的方案(createJuiceMachineGuide)。
在JuiceMachineFactory中,通过向createJuiceMachineGuide传入参数,可以获得能生产特定刀头的榨汁机的方法_createJuiceMachine,而通过执行_createJuiceMachine,则可以生产出特定刀头的榨汁机。对外表现出来的逻辑就是:JuiceMachineFactory可以确定生产出来的榨汁机所使用的刀头,并且能够生产榨汁机。
用代码表示如下:
var JuiceMachineFactory = function(){ var newFactory = {}; var _cutterFn = function(){ //榨汁机工厂当前使用的榨汁方法,相当于定义了一个特定的刀头 console.log( \'正在使用:A型刀头。\' ); }; //获得一种制造榨汁机的方法 var _createJuiceMachine = createJuiceMachineGuide( _cutterFn ); //制造了一台榨汁机 newFactory.createMachine = function(){ return _createJuiceMachine(); } return newFactory; }; var createJuiceMachineGuide = function( cutterFn ){ return function(){ var newMachine = {}; var _cutterInMachine = cutterFn ; //用传入的\'刀头\'作为制造榨汁机的原件 newMachine.makeJuice = function(){ //开始榨汁 console.log( \'##开始榨汁:\' ); //调用传入的私有变量 _cutterInMachine(); } return newMachine ; } } //先创建唯一的一座工厂: var factory = JuiceMachineFactory(); //现在,我们生产一台榨汁机: var machine_A = factory.createMachine(); //使用这台榨汁机 machine_A.makeJuice( );
运行代码之后输出如下结果:
##开始榨汁:
正在使用:A型刀头。
【分析】
1. 和我们预期的一样,我们通过传入_cutterFn来获得一个\'采用刀头A来进行榨汁\'的榨汁机制造方案,并且用这个方案制造了一台榨汁机。
2. 在使用新造的这台榨汁机进行榨汁的时候,确实是使用了刀头A的方法。
>> 通俗地讲,工厂制造这台榨汁机时,选用了哪种\'制造方案\',决定了生产出来的榨汁机的特性。
2》工厂选用了B型刀头方案制造榨汁机
经过一段时间的发展,市场人员反馈说,现在用户都希望使用B型的刀头,我们得修改制造一下制造榨汁机的方案了。但是,从目前来看,我们并没有预留\'修改榨汁机刀头\'的接口,所以需要调整一下工厂,使它能够方便选择采用哪种刀头方案,调整后的代码如下:
var JuiceMachineFactory = function(){ var newFactory = {}; var _cutterFn = function(){ //榨汁机工厂当前使用的榨汁方法,相当于定义了一个特定的刀头 console.log( \'正在使用:A型刀头。\' ); }; //获得一种制造榨汁机的方法 var _createJuiceMachine = createJuiceMachineGuide( _cutterFn ); //制造了一台榨汁机 newFactory.createMachine = function(){ return _createJuiceMachine(); } //修改新的制造榨汁机的方案 newFactory.setCutterFn = function( new_fn ){ _cutterFn = new_fn ; return true; } return newFactory; }; var createJuiceMachineGuide = function( cutterFn ){ return function(){ var newMachine = {}; var _cutterInMachine = cutterFn ; //用传入的\'刀头\'作为制造榨汁机的原件 newMachine.makeJuice = function(){ //开始榨汁 console.log( \'##开始榨汁:\' ); //调用传入的私有变量 _cutterInMachine(); } return newMachine ; } } //先创建唯一的一座工厂: var factory = JuiceMachineFactory(); //现在,我们生产一台榨汁机: var machine_A = factory.createMachine(); //使用这台榨汁机 machine_A.makeJuice( ); //设置一下工厂的制造方案 factory.setCutterFn( function(){ console.log( \'正在使用:B型刀头。\' ); }); //用新的方案再制造一台榨汁机 var machine_B = factory.createMachine(); machine_B.makeJuice( );
运行代码之后输出如下结果:
##开始榨汁:
正在使用:A型刀头。
##开始榨汁:
正在使用:A型刀头。
【分析】
1. 采用默认的工艺,制造出来的榨汁机,采用的是 A型刀头。
2. 后面通过setCutterFn( ) 调整了制造方案,生产出来的榨汁机,按理,应该是采用“B型刀头”了啊!为什么还是A型刀头?!
这个问题引出了JavaScript的一个知识点:JavaScript中的对象的赋值,是一种\'引用\'传递。
我们分析一下整个过程。
1. 下面的这段代码执行之后,变量_cutterFn引用了一个\'函数对象\',我们给这个函数对象一个编号:F_007。
var _cutterFn = function(){ //榨汁机工厂当前使用的榨汁方法,相当于定义了一个特定的刀头 console.log( \'正在使用:A型刀头。\' ); };
示意图如下:
2. 执行下面的这段代码之后,相当于变量_cutterFn 和 _cutterInMachine 都指向了 F007 这个函数对象。
var _createJuiceMachine = createJuiceMachineGuide( _cutterFn );
示意图如下:
3. 当我们向用新的方案来制造B型刀头榨汁机,在前面的代码中,我们用了setMakeMachineMethod方法,代码如下:
//设置一下工厂的制造方案 factory.setMakeMachineMethod( function(){ console.log( \'正在使用:B型刀头。\' ); });
此时各个变量所指的对象如下所示:
因为只是赋值仅仅是变量之间的\'引用\'传递,所以,执行完毕之后,变量_cutterFn 和 _cutterInMachine 并没有必然的联系!也就是说,_cutterFn 指向了别的地方(代号为F008的新对象),_cutterInMachine 并不会随之改变,除非再来一次赋值。
在我们的例子,我们要改变的其实不是_cutterFn所指的对象,我们应该改变 _cutterInMachine 所指的对象。也就是要重新\'赋一次\'值,生成一个新的方案_createJuiceMachine。调整后的代码如下:
var JuiceMachineFactory = function(){ var newFactory = {}; var _cutterFn = function(){ console.log( \'正在使用:A型刀头。\' ); }; //获得一种制造榨汁机的方法 var _createJuiceMachine = createJuiceMachineGuide( _cutterFn ); //制造了一台榨汁机 newFactory.createMachine = function(){ return _createJuiceMachine(); } //修改新的制造榨汁机的方案 newFactory.setMakeMachineMethod = function( new_fn ){ _cutterFn = new_fn ; //关键是修改下面的内容 _createJuiceMachine = createJuiceMachineGuide( _cutterFn ); return true; } return newFactory; }; var createJuiceMachineGuide = function( cutterFn ){ return function(){ var newMachine = {}; var _cutterInMachine = cutterFn ; //用传入的\'刀头\'作为制造榨汁机的原件 newMachine.makeJuice = function(){ //开始榨汁 console.log( \'##开始榨汁:\' ); //调用传入的私有变量 _cutterInMachine(); } return newMachine ; } } //先创建唯一的一座工厂: var factory = JuiceMachineFactory(); //现在,我们生产一台榨汁机: var machine_A = factory.createMachine(); //使用这台榨汁机 machine_A.makeJuice( ); //设置一下工厂的制造方案 factory.setMakeMachineMethod( function(){ console.log( \'正在使用:B型刀头。\' ); }); //用新的方案再制造一台榨汁机 var machine_B = factory.createMachine(); machine_B.makeJuice( ); //再看一下原来制造的那台machine_A,是否还是使用了A型刀头? //答案是还是A型刀头,因为每执行一次createJuiceMachineGuide,其实是新增一个闭包! //也就是说, //制造machine_A的那套方案,并没有消失,它的榨汁的方法,还是指向F007那个函数对象。 //而制造machine_B的那套方案,榨汁方法指向了F008这个函数对象。 machine_A.makeJuice( );
运行代码之后输出如下结果:
##开始榨汁:
正在使用:A型刀头。
##开始榨汁:
正在使用:B型刀头。
##开始榨汁:
正在使用:A型刀头。
这回就正确了!
在生成对象machine_A和machine_B的时候,实际上我们调用了两次createJuiceMachineGuide,生成了两个独立的\'运行时环境\',而在这两次调用的过程中,运行时环境中的_cutterInMachine私有变量所指向的对象是不一样的。
整个过程示意如下:
【小节】
1. JavaScript中的赋值、参数传递都是\'引用\'传递。
"这个好理解不?",
"好理解。"
2. 执行一次\'闭包\'函数,会生成一个独立的\'运行时环境\'。
"这个好理解不?"
"好理解哈,相当于返回一个对象,这个对象中的函数可以访问\'闭包\'函数中的私有成员。"
把它们放到一起综合起来理解,就可以很好的解释"工厂可以决定生产出来的榨汁机采用哪种\'刀头\'了。"
2》给榨汁机添加\'水果\'
截止目前为止,我们在\'榨汁机\'这个模型中,其实还仅仅定义了\'榨汁方法\',并且确定,采用哪种榨汁方法(选用哪种刀头),是在工厂设置\'制造方案\'是决定的。但是,完整的榨汁过程,应该是有\'水果\'的,并且,\'水果\'应该是可以替换的。
用代码描述如下:
var JuiceMachineFactory = function(){ var newFactory = {}; var _cutterFn = function(){ console.log( \'正在使用:A型刀头。\' ); }; //获得一种制造榨汁机的方法 var _createJuiceMachine = createJuiceMachineGuide( _cutterFn ); //制造了一台榨汁机 newFactory.createMachine = function(){ return _createJuiceMachine(); } //修改新的制造榨汁机的方案 newFactory.setMakeMachineMethod = function( new_fn ){ _cutterFn = new_fn ; //关键是修改下面的内容 _createJuiceMachine = createJuiceMachineGuide( _cutterFn ); return true; } return newFactory; }; var createJuiceMachineGuide = function( cutterFn ){ return function(){ var newMachine = {}; var _cutterInMachine = cutterFn ; //用传入的\'刀头\'作为制造榨汁机的原件 var _fruit = \'橙子\' ; //默认是橙子汁
newMachine.selectFruit = function( new_fruit ){ _fruit = new_fruit ; } newMachine.makeJuice = function( ){ //开始榨汁 console.log( \'##开始榨汁:\' ); console.log( \'使用水果<\' +_fruit+\'>\' ); //调用传入的私有变量 _cutterInMachine(); } return newMachine ; } } //先创建唯一的一座工厂: var factory = JuiceMachineFactory(); //现在,我们生产一台榨汁机: var machine_A = factory.createMachine(); //使用这台榨汁机 machine_A.makeJuice( ); //选择另外的水果 machine_A.selectFruit( \'西瓜\' ); //再次榨水果 machine_A.makeJuice( );
运行代码之后输出如下结果:
##开始榨汁: 使用水果<橙子> 正在使用:A型刀头。 ##开始榨汁: 使用水果<西瓜> 正在使用:A型刀头。
【分析】
与我们预期的一样:默认情况下,我们使用<橙子>来榨果汁,因为我们也开放了操纵_fruit的接口,所以,我们也可以使用<西瓜>来榨果汁。
【小节】
到目前为止,我们掌握了以下两个情况的处理:
1. 在工厂中可以根据需要调整制造方案:
我们通过传入参数的方式,结合闭包的特性,更换_createJuiceMachine的值,用另外一个\'闭包\'的运行结果代替之前的\'闭包运行结果\'。
由于两个运行结果中的_cutterInMachine的值不同,相当于产生了两套不同的制造榨汁机的方法。
>>
回到举例的场景中:制造榨汁机的工厂,可以决定生产出来的榨汁机采用哪种\'刀头\'。
对于JavaScript本身,由于\'高阶函数\'的特点,相当于通过传入参数,可以定制\'闭包\'函数本身。
举例中:
createJuiceMachineGuide:制造\'闭包\'函数的函数。
_createJuiceMachine:就是执行createJuiceMachineGuide之后返回的\'闭包\'函数。
var machine_A = factory.createMachine();:machine_A,执行_createJuiceMachine之后的返回的一个\'闭包\'。
相关知识点:闭包,高阶函数特性,JavaScript赋值操作的本质。
2. 在榨汁时,可以设定榨汁采用的\'水果\'。
这相当于给了我们一个从\'业务模型\'认识\'闭包\'函数的好例子:
a. 运行\'闭包\'函数,会产生一个\'对象\'(或函数定义)。这个\'对象\'中,原来定义在\'闭包\'函数中的局部变量和参数,成为这个\'对象\'的私有成员。而这个\'对象\'中的方法,则成为\'对象\'的对外接口(公共函数),这些对外接口可以访问\'对象\'的私有成员。当然,一般不会在返回的\'对象\'中直接设置\'属性\'成员,因为那样是\'违背\'封装性的原则的。
b. 设计一个\'闭包\'函数的时候:
1> 如果某个成员是业务对象的属性(例如:_fruit),那么,就把它声明为\'闭包\'函数的局部变量或通过参数传入。
2> 如果某个函数并不想开放给别人使用(例如:_cutterInMachine),那么也把它声明为\'闭包\'函数的局部变量或通过参数传入。
3> 业务对象对外呈现出来的功能(例如:makeJuice),则把它声明为该对象的\'成员\'函数。
4> \'闭包\'函数中的\'私有成员\',可以通过对外开放出来的公共函数(makeJuice和selectFruit)更改。
讨论到这里,借助\'闭包\'和\'高阶函数\'的特性,我们似乎已经能够实现应用中的\'业务\'建模。我们定义好一个\'闭包函数\'之后,就可以\'产生满足我们业务要求\'的对象,并且,这些对象具有很好的\'封装性\'。
到现在为止,我们还没有用到this,没有this的日子,似乎也是很美好的。
3》给榨汁机增加云端系统
现在的社会,当我们讲到家电设备时,我们往往会说‘智能家电’,取名称时喜欢带一个\'云\'字,例如:云冰箱,云电视。其实质就是,每个家电背后都有一个\'云端\'系统,通过\'云端\'系统,我们可以\'远程\'与我们的家电通信。比如,还没有下班,我们知道今天下午有一场AC米兰的球赛。这时候,借助\'云端\'系统,我们可以通过手机遥控家里的\'云电视\',让它把AC米兰的球赛先录下来,这样,我们下班回到家就可以观看。
现在,我们要给榨汁机也增加这样的\'云端\'系统,这样,我们就可以在下班回家的路上远程操控榨汁机,一回到家,就能喝到\'新鲜\'的果汁。比起那些只有\'定时\'功能的家电,\'云家电\'还是要方便很多,比如:你如果发现路上堵车了,就可以晚点启动\'榨汁作业\'。如果老板告诉你晚上要加一下班,你就可以不启动\'榨汁作业\'。
好了,现在我们就给榨汁机增加一个\'云控制系统\',然后增加一个\'远程启动\'的功能。
var JuiceMachineFactory = function(){ var newFactory = {}; var _cutterFn = function(){ console.log( \'正在使用:A型刀头。\' ); }; //获得一种制造榨汁机的方法 var _createJuiceMachine = createJuiceMachineGuide( _cutterFn ); //制造了一台榨汁机 newFactory.createMachine = function(){ return _createJuiceMachine(); } //修改新的制造榨汁机的方案 newFactory.setMakeMachineMethod = function( new_fn ){ _cutterFn = new_fn ; //关键是修改下面的内容 _createJuiceMachine = createJuiceMachineGuide( _cutterFn ); return true; } return newFactory; }; var createJuiceMachineGuide = function( cutterFn ){ return function(){ var newMachine = {}; var _cutterInMachine = cutterFn ; //用传入的\'刀头\'作为制造榨汁机的原件 var _fruit = \'橙子\' ; //默认是橙子汁 var _cloudCenter = { remoteStart:function(){ //这里模拟执行远程启动时,云端控制\'榨汁机\'开始榨汁 console.log( \'^^云中心收到指令,马上控制榨汁机开始榨汁^^\' ); newMachine.makeJuice(); return ; } } newMachine.selectFruit = function( new_fruit ){ _fruit = new_fruit ; } newMachine.makeJuice = function( ){ //开始榨汁 console.log( \'##开始榨汁:\' ); console.log( \'使用水果<\' +_fruit+\'>\' ); //调用传入的私有变量 _cutterInMachine(); } //增加远程启动榨汁机的功能 newMachine.remoteStart = function(){ //模拟这个功能要借助云端中心执行 _cloudCenter.remoteStart(); } return newMachine ; } } //先创建唯一的一座工厂: var factory = JuiceMachineFactory(); //现在,我们生产一台榨汁机: var machine_A = factory.createMachine(); //使用这台榨汁机 machine_A.remoteStart( );
运行代码之后输出如下结果:
^^云中心收到指令,马上控制榨汁机开始榨汁^^ ##开始榨汁: 使用水果<橙子> 正在使用:A型刀头。
【分析】
嘿嘿,看来增加一个云端的系统也是很简单的嘛。没错,这样的设计目前来看是符合我们的预期的。但是,我们是一个\'榨汁机\'的工厂,这就意味着我们不是\'仅仅\'生产一台\'榨汁机\',我们可能会生产上千台,上万台这样的榨汁机。这样,我们再次从\'空间\'分配的角度来剖析整个榨汁机的过程。
运行下面的代码:
var machine_A = factory.createMachine();
相当于执行了_createJuiceMachine这样的闭包函数,我们知道:"每执行一次\'闭包\'函数,JavaScript引擎会为闭包函数中的\'局部变量\'分配一次空间。"
执行1次\'闭包\'函数factory.createMachine()之后和执行n次\'闭包\'函数之后的花费空间示意:
假设_cloudCenter这个局部变量占据了很大的空间,我们用深颜色标识出来。回到业务场景,搭建一个\'云中心\'需要大量的资金投入,但是,我们现在的搞法就相当于:
"为生产出来的每一台榨汁机,都搭建一个专属的云中心。"
显然,这是不合理的。
有同学可能会说,对于_cloudCenter这个局部变量,我们没有必要在\'闭包\'函数的函数体内专门全新生成一个对象,可以引用\'外部\'的一个唯一对象,这样就可以节省大量的空间。
这种方式确实也是一种解决方案,在之前的\'地址选择控件开发\'这篇博文中,针对全国地址信息模型这个比较大的对象,我们就是采用这种方式。
我们再仔细审视一下_createJuiceMachine这个闭包函数,除了_fruit之外,似乎其他的方法也都很_cloudCenter一样,没有必要每生成一个对象,就全新打造一份!
综合上面的分析,我们可以使用JavaScript的\'原型继承\'特性,来完成我们的业务目标。
我们先来看一下,优化之前,machine_A, machine_B......这些对象的\'原型链\'关系。
如果基于\'原型继承\'来实现我们的业务目标,那么,我们希望我们的\'原型链\'关系会变成如下所示:
有一个BaseMachine,具有我们\'期望\'的功能。新创建的machine_A, machine_B都\'原型继承\'自BaseMachine。
下面,我们用代码来实现这样的业务:
var JuiceMachineFactory = function(){ var newFactory = {}; var _cutterFn = function(){ console.log( \'正在使用:A型刀头。\' ); }; //获得一种制造榨汁机的方法 var _createJuiceMachine = createJuiceMachineGuide( _cutterFn ); //先制造一个\'原型对象\' var _base_machine = _createJuiceMachine(); //制造了一台榨汁机 newFactory.createMachine = function(){ //基于\'原型\'对象来创建新的榨汁机 var new_machine = Object.create( _base_machine ); new_machine.setDefaultFruit(); return new_machine; } return newFactory; }; var createJuiceMachineGuide = function( cutterFn ){ return function(){ var newMachine = {}; var _cutterInMachine = cutterFn ; //用传入的\'刀头\'作为制造榨汁机的原件 var _fruit = \'橙子\' ; //默认是橙子汁 var _cloudCenter = { remoteStart:function(){ //这里模拟执行远程启动时,云端控制\'榨汁机\'开始榨汁 console.log( \'^^云中心收到指令,马上控制榨汁机开始榨汁^^\' ); //注意:因为在makeJuice中使用了this.fruit, //所以,这里也要用this来调用,否则,makeJuice中的this所指对象就不正确! this.makeJuice(); return ; } } newMachine.selectFruit = function( new_fruit ){ this.fruit = new_fruit ; } //看一下当前使用的苹果 newMachine.showFruit = function( ){ console.log( \'>>正在使用的水果是:\' + this.fruit ); return ; } newMachine.setDefaultFruit = function(){ this.fruit = _fruit; //设置新的对象使用默认的水果 } newMachine.makeJuice = function( ){ //开始榨汁 console.log( \'##开始榨汁:\' ); console.log( \'使用水果<\' +this.fruit+\'>\' ); //调用传入的私有变量 _cutterInMachine(); } //增加远程启动榨汁机的功能 newMachine.remoteStart = function(){ //模拟这个功能要\'借助\'云端中心执行 //相当于要\'借用\'_cloudCenter.remoteStart这个函数,借用函数怎么搞,用apply哈 _cloudCenter.remoteStart.apply( this , arguments ); } return newMachine ; } } //先创建唯一的一座工厂: var factory = JuiceMachineFactory(); //现在,我们生产一台榨汁机: var machine_A = factory.createMachine(); //设置一下水果的值 machine_A.selectFruit( \'苹果\' ); //查看一下当前所使用的水果 machine_A.showFruit( ); //使用这台榨汁机 machine_A.remoteStart( ); //再全新生成一个对象machine_B // var machine_B = factory.createMachine(); machine_B.makeJuice( );
运行代码之后输出如下结果:
>>正在使用的水果是:苹果 ^^云中心收到指令,马上控制榨汁机开始榨汁^^ ##开始榨汁: 使用水果<苹果> 正在使用:A型刀头。 ##开始榨汁: 使用水果<橙子> 正在使用:A型刀头。
【分析】
就是为了使用\'原型继承\'这个特性,我们引入了this,为了达到各种预期的效果,我们对各个函数都进行了调整。
1. 工厂中,生成machine对象的方式,调整成:先生成一个原型对象(_base_machine),然后基于这个原型对象创建全新的对象。
2. 基于原型对象创建的全新对象,自己是没有任何\'成员属性\'的。
所以,我们通过调用new_machine.setDefaultFruit();来为新创建的对象添加自己的成员属性fruit,而它的值就是原型对象(_base_machine)中闭包空间中的值_fruit,这样符合我们的业务预期。
3. 将原型对象中,对外公开方法中_fruit的地方,都换成了this.fruit,因为我们希望,当我们使用原型对象_base_machine中的对外公开方法时,其中的fruit应该指代全新创建的对象的fruit成员,而不是生成_base_machine时,闭包中存在的_fruit。
4. 原型对象的对外公开方法(remoteStart),其实调用的不是原型对象本身拥有的方法,而是\'借用\'了\'第三方对象\'_cloudCenter中的方法。如果不做调整,直接调用_cloudCenter.remoteStart();那么,函数体的所指代的this就会指向了_cloudCenter。如何向别的对象\'借用\'方法,我们在前面的博客\'闲聊JS中的apply和call\'中有比较详细的介绍,所以,我们采用如下的方式调用:
_cloudCenter.remoteStart.apply( this , arguments );
这样,就确保了当你执行new_machine.remoteStart()时,_cloudCenter.remoteStart函数体中的this也是指代new_machine,与我们的业务预期相符。
经过一番周折,我们终于利用JavaScript的\'原型继承\'特性,实现了我们的\'业务目标\'。JavaScript的函数调用特性,使得JavaScript更具有\'弹性\',当然,也使实现逻辑变得更加复杂。当你\'迫不得已\'要在函数体中使用this的时候,你一定要想清楚,到时候真正调用这个函数的对象会是谁,它具有什么样的成员属性。
回顾整个过程,下面这个语句,其实是用到了一定的随意性,或者说是JavaScript语言的\'弹性\'。
newMachine.setDefaultFruit = function(){ this.fruit = _fruit; //设置新的对象使用默认的水果 }
如果不存在,那么就给对象新增一个fruit成员属性。不过问题不大,我们把这个随意性控制在selectFruit这个方法当中了。现在再回到第2个场景"工厂选用了B型刀头方案制造榨汁机",因为我们使用了\'原型继承\',要换一种榨汁方式,就需要修改原型对象中\'运行时环境\'中的_cutterInMachine变量。到底要如何修改呢?就留作练习吧。
【总结】
今天我们以\'榨汁机工厂\'生产特定功能的\'榨汁机\'为场景,讲解了如何用JavaScript的\'闭包\'特性进行\'业务建模\'。首先我们采用了更改\'_createJuiceMachine\'的方式达到更换榨汁方式的目的。后来,我们给生产的\'榨汁机\'增加了\'云\'功能。当增加了\'云\'功能之后,发现从\'空间利用\'的角度看,原来的生产\'榨汁机\'的方式存在一些问题。最后,我们利用了JavaScript的\'原型继承\'特性,解决了\'空间利用\'问题。\'榨汁机\'只是我们讲故事的道具,真正想表达的是JavaScript的各种特性。
下次如果有时间,我们一起聊聊JavaScript社区中的设计模式。这里先举一个小故事。
有一天,老板对小明说,“小明啊,周末我们超市苹果打特价,6.98一斤,你画一张海报宣传一下。”于是,小明设计了下面的一张海报:
老板说,“设计得不错,果然身手不凡哈!这样,你再画1000份,明天周五给你放一天假,你去东门路口把画好的1000份宣传单发了。”
"再画1000份?!"
现在已经是周四下午5点,你觉得小明是会拿起画笔赶紧开始画呢?还是拿起画笔赶紧开始画呢?当然不会啦,小明肯定会选择拿着之前画好的宣传海报,去扫描复印1000份。
生活中会遇到许多的\'迫不得已\',所以,我们要学会聪明做事,善待自己。只有聪明地做事,才能使用户满意,老板开心,自己也不闹心。而对于我们写代码的人来说,了解一些\'设计模式\',也许能使自己做到:聪明地做事情,优雅地写代码,从容面对各种\'迫不得已\'。
感谢诸位捧场,谢谢。
以上是关于直播开始:'云榨汁机'诞生记--聊聊JavaScript中的'业务建模'的主要内容,如果未能解决你的问题,请参考以下文章