孵化园三个月的学习时间让我学到了很多,更让我发现了更多感兴趣的东西,促使着我不断向前,不敢止步。三个月朝着一个方向认真学习,足以有些成效,所有,公司对我们进行了一次期中考试,检验我们学习效果的同时,也提醒了我要进行一次自我总结。本篇博客将对此次期中考学到的东西进行整理总结,并对接下来的学习做一些规划。
某天突然接到消息说公司会对我们进行一次期中考试,兴奋的同时也带着一些紧张,兴奋着即将有机会去向往的TW观摩观摩,紧张着自己的实力是否能得到认可或者说自己目前的实力是否能让HR满意。伴随着紧张期待的心情拿到homework之后,心态却不自觉的平静下来,开始一心钻研需求了,毕竟English Ability还有些low。
读懂需求之后,我并没有按照面向对象的思路来分列出可以抽取的对象,而是直接按照需求来写测试并实现,即TDD。这一块是我目前比较模糊的,我到底应不应首先对需求的总体进行分析,进而罗列出能够抽离出来的对象、类,还是直接按照需求,一小步一小步的来进行Test Driven Development。
当我把整个homework先写测试再实现以后发现现在的程序没有面向对象而是面向过程,这个时候我便着手重构的第二阶段,对项目结构进行重构。将字符串转化为对象那一块提取到ItemsTransformation类中,将对item进行一系列操作的方法提取到ItemStudio类中,再将把item对象输出为字符的方法提取为BuildReceiptView类中,由此,我便有了三个类,执行顺序也很明了,ItemsTransformation -> ItemsStudio -> BuildReceiptView。
接着,我再修改了数据源的获取方式,使该项目有一个支持文本文件输入的通道(There must be a way to supply the application with the input data via text file)。最后,便是建立了类似main的printInventory,来将购物清单打印出来。
整个流程目前来看还是挺简单清晰的,不过,经公司老师一审查,不由漏洞百出的感觉...
1、讲述方式
可能由于紧张的缘故,让我讲述我的项目的时候,我陷入了细节坑,花了十分钟左右来讲述各个流程细节的实现,但是效果并不好。在讲述一个项目的时候,应该有高屋建瓴的感觉,从一个较高的层次来对项目进行说明。比如,整个项目的流程,每个模块的输入输出及其功能等等,这样的描述让人易于理解。这种思想,《金字塔原理》中有提及,但是当时就是没用到。由此可见,思维表述的方式我还有待加强。
2、源代码命名方式
我整个项目中使用的都是带下划线的命名方式,经老师说明,在源代码中尽量都使用驼峰命名的方式,测试文件中才使用下划线的命名。此外,类名应为名词,方法名为动词,这些都是最基本的。
3、安全问题
项目中所用到的数据,例如商品基税,应该赋值给变量再由变量去对商品加以赋值,而不应该直接赋值给商品,这样会与普通的数字没有区别,可能会出现安全问题,而且以后也不方便修改。
4、函数职责
函数功能应该单一,这是一直在强调的问题,可是自己还是犯了。经老师一提点,才发现有一个方法确实有些复杂臃肿,而自己看的时候居然没怎么发现。由此,以后对函数功能的检查也应该增强。
5、面向对象
老师说从我的程序来看,面向对象的感觉不强,但是紧张的我当时都忘了请教这个项目如何才能做出面向对象的感觉了。嗯,不过这给以后的修行增加了课题,便是面向对象。
关于字符串的转化,之前的代码是这样的
1 function Transfer_input_to_basic_items(some_items_info){ 2 this.some_items_info = some_items_info 3 } 4 5 Transfer_input_to_basic_items.prototype.get_array_string_of_item_info = function () { 6 this.some_items_info = this.some_items_info.split(' '); 7 }; 8 9 Transfer_input_to_basic_items.prototype.get_basic_items = function () { 10 this.get_array_string_of_item_info(); 11 var basic_items = []; 12 function build_item_from_input_string(one_info) { 13 var item = {}; 14 var one_item_info = one_info.split(" at ", 2); 15 var count_and_name_info = one_item_info[0].split(" "); 16 var name_info = _.last(count_and_name_info, count_and_name_info.length - 1); 17 var name_from_one_info = _.reduce(name_info, function (memo, str) { 18 return memo + ' ' + str; 19 }, ''); 20 item.name = name_from_one_info.trim(); 21 item.count = parseInt(count_and_name_info[0]); 22 item.price = parseFloat(one_item_info[1].trim()); 23 item.summary = item.count * item.price; 24 return item 25 } 26 _(this.some_items_info).each(function (item_info){ 27 basic_items.push(build_item_from_input_string(item_info)); 28 }); 29 return basic_items 30 };
可以看到,命名方式均以下划线方式命名,参数名字也不好,还有局部函数,这个局部函数是我自己的问题,把一个简单问题复杂化了,也可以说是我对字符串处理能力不足。下面上回来之后重构的,
1 function ItemsTransformation(someItemsInfo) { 2 this.someItemsInfo = someItemsInfo 3 } 4 5 ItemsTransformation.prototype._getArrayStringOfItemInfo = function () { 6 this.someItemsInfo = this.someItemsInfo.split(' '); 7 }; 8 9 ItemsTransformation.prototype.getBasicItems = function () { 10 this._getArrayStringOfItemInfo(); 11 var basicItems = []; 12 _(this.someItemsInfo).each(function (itemInfo) { 13 var item = {}; 14 item.name = itemInfo.slice(itemInfo.indexOf(' ') + 1, itemInfo.indexOf(' at ')); 15 item.count = parseInt(itemInfo.slice(0, itemInfo.indexOf(' '))); 16 item.price = parseFloat(itemInfo.slice(itemInfo.indexOf(' at ') + 4, itemInfo.length)); 17 item.summary = item.count * item.price; 18 basicItems.push(item); 19 }); 20 return basicItems 21 };
可以看到,函数、变量命名方式全部改为驼峰了,并且优化了算法,去掉了那个看起来怪怪的局部方法。下面再看看之前功能不单一的类,
1 function Items_studio(items) { 2 this.items = items; 3 } 4 5 Items_studio.prototype._add_basic_sales_tax_rate = function (one_item) { 6 one_item.basic_tax_rate = 0.1; 7 _(loadAllExemptionBasicItemsCategory).each(function (ExemptionBasicItemsCategory) { 8 _(ExemptionBasicItemsCategory.name).each(function (name) { 9 if (one_item.name == name) { 10 one_item.basic_tax_rate = 0 11 } 12 }) 13 }); 14 }; 15 16 Items_studio.prototype._add_import_duty = function (one_item) { 17 one_item.import_duty_rate = 0; 18 if (one_item.name.indexOf('imported ') > -1) { 19 one_item.import_duty_rate = 0.05 20 } 21 }; 22 23 Items_studio.prototype._add_after_tax_summary = function (one_item) { 24 var calculator = new Calculator; 25 var taxes = calculator.basic_tax_value(one_item) + calculator.import_duty_value(one_item); 26 taxes = parseFloat(taxes.toString().substring(0, taxes.toString().indexOf('.') + 3)); 27 if (parseInt(taxes.toString().substring(3, 4)) < 5 && parseInt(taxes.toString().substring(3, 4)) != 0) { 28 taxes = parseFloat(taxes.toString().substring(0, 3) + '5'); 29 one_item.after_tax_summary = taxes + one_item.summary; 30 } else { 31 one_item.after_tax_summary = parseFloat((Math.round(taxes * 10) / 10 + one_item.summary).toFixed(2)); 32 } 33 }; 34 35 Items_studio.prototype.get_receipt_items = function () { 36 var self = this; 37 _(this.items).each(function (one_item) { 38 self._add_basic_sales_tax_rate(one_item); 39 self._add_import_duty(one_item); 40 self._add_after_tax_summary(one_item); 41 }); 42 return this.items 43 }; 44 45 module.exports = Items_studio;
可以看到_add_after_tax_summar()这个方法确实是有些臃肿不美观了,因为这个方法做了得到税金以及对税金按照取舍规则进行取舍,下面上回来之后重构的这部分代码,
ItemsStudio.prototype._addAfterTaxSummary = function (item) { var calculator = new Calculator; var taxes = calculator.getBasicTaxValue(item) + calculator.getImportDutyValue(item); taxes = parseFloat(taxes.toString().substring(0, taxes.toString().indexOf('.') + 3)); item.afterTaxSummary = (this._getValueFromRule(taxes) + item.summary).toFixed(2); }; ItemsStudio.prototype._getValueFromRule = function (taxes) { if (parseInt(taxes.toString().substring(3, 4)) < 5 && parseInt(taxes.toString().substring(3, 4)) != 0) { taxes = parseFloat(taxes.toString().substring(0, 3) + '5'); return taxes } else { return Math.round(taxes * 10) / 10 } };
这样看起来不仅干净了许多,而且可读性也得到了大大的增强。
经此一役,发现自己还有很多可提高的部分,
1、思维表述方式。准备再啃啃《金字塔原理》,将里面所介绍的方法进行自我练习。
2、面向对象。面向对象的思想还很浅薄,这也是目前我的一个瓶颈,攻克此瓶颈的方法便是多读书,多读一些关于介绍面向对象思想的书。
3、提高自我要求。对coding中出现的任何臃肿都应立即着手解决。
总的来说,小菜鸟一枚,但我是有理想有目标的小菜鸟,哈哈哈~
ps:文中若是有哪里理解不顺或是需要改进的地方,望指出~