Write small and plain functions
"do one thing and do it well".
dedicate time not only to implementation, but also to a correct structure of your functions
https://rainsoft.io/the-art-of-writing-small-and-plain-functions/?utm_source=codropscollective
Measure seven times, cut once.
假设一个场景:当一个功能函数需要通过返回"数组","map","简单js对象"去计算他的特性的值。
* 1 point for null or undefined
* 2 points for a primitive type
* 4 points for an object or function.
primitive type:简单键值对对象
function getCollectionWeight(collection) {
let collectionValues;
if (collection instanceof Array) {
collectionValues = collection;
} else if (collection instanceof Map) {
collectionValues = [...collection.values()];
} else {
collectionValues = Object.keys(collection).map(function (key) {
return collection[key];
});
}
return collectionValues.reduce(function(sum, item) {
if (item == null) {
return sum + 1;
}
if (typeof item === 'object' || typeof item === 'function') {
return sum + 4;
}
return sum + 2;
}, 0);
}
let myArray = [null, { }, 15];
let myMap = new Map([ ['functionKey', function() {}] ]);
let myObject = { 'stringKey': 'Hello world' };
getCollectionWeight(myArray); // => 7 (1 + 4 + 2)
getCollectionWeight(myMap); // => 4
getCollectionWeight(myObject); // => 2
getCollectionWeight()
Now the goal is to split the big function into smaller, independent and reusable ones
function getWeightByType(value) {
const WEIGHT_NULL_UNDEFINED = 1;
const WEIGHT_PRIMITIVE = 2;
const WEIGHT_OBJECT_FUNCTION = 4;
if (value == null) {
return WEIGHT_NULL_UNDEFINED;
}
if (typeof value === 'object' || typeof value === 'function') {
return WEIGHT_OBJECT_FUNCTION;
}
return WEIGHT_PRIMITIVE;
}
function getCollectionWeight(collection) {
let collectionValues;
if (collection instanceof Array) {
collectionValues = collection;
} else if (collection instanceof Map) {
collectionValues = [...collection.values()];
} else {
collectionValues = Object.keys(collection).map(function (key) {
return collection[key];
});
}
return collectionValues.reduce(function(sum, item) {
return sum + getWeightByType(item);
}, 0);
}
let myArray = [null, { }, 15];
let myMap = new Map([ ['functionKey', function() {}] ]);
let myObject = { 'stringKey': 'Hello world' };
getCollectionWeight(myArray); // => 7 (1 + 4 + 2)
getCollectionWeight(myMap); // => 4
getCollectionWeight(myObject); // => 2
if (item == null) {
return sum + 1;
}
if (typeof item === 'object' ||
typeof item === 'function') {
return sum + 4;
}
return sum + 2;
function getWeightByType(value) {
const WEIGHT_NULL_UNDEFINED = 1;
const WEIGHT_PRIMITIVE = 2;
const WEIGHT_OBJECT_FUNCTION = 4;
if (value == null) {
return WEIGHT_NULL_UNDEFINED;
}
if (typeof value === 'object' ||
typeof value === 'function') {
return WEIGHT_OBJECT_FUNCTION;
}
return WEIGHT_PRIMITIVE;
}
// Code extracted into getMapValues()
function getMapValues(map) {
return [...map.values()];
}
// Code extracted into getPlainObjectValues()
function getPlainObjectValues(object) {
return Object.keys(object).map(function (key) {
return object[key];
});
}
function getCollectionWeight(collection) {
let collectionValues;
if (collection instanceof Array) {
collectionValues = collection;
} else if (collection instanceof Map) {
collectionValues = getMapValues(collection);
} else {
collectionValues = getPlainObjectValues(collection);
}
return collectionValues.reduce(function(sum, item) {
return sum + getWeightByType(item);
}, 0);
}
else if (collection instanceof Map) {
collectionValues = [...collection.values()];
} else {
collectionValues = Object.keys(collection)
.map(function (key) {
return collection[key];
});
}
function getWeightByType(value) {
const WEIGHT_NULL_UNDEFINED = 1;
const WEIGHT_PRIMITIVE = 2;
const WEIGHT_OBJECT_FUNCTION = 4;
if (value == null) {
return WEIGHT_NULL_UNDEFINED;
}
if (typeof value === 'object' ||
typeof value === 'function') {
return WEIGHT_OBJECT_FUNCTION;
}
return WEIGHT_PRIMITIVE;
}
function getMapValues(map) {
return [...map.values()];
}
function getPlainObjectValues(object) {
return Object.keys(object)
.map(function (key) {
return object[key];
});
}
function getCollectionValues(collection) {
if (collection instanceof Array) {
return collection;
}
if (collection instanceof Map) {
return getMapValues(collection);
}
return getPlainObjectValues(collection);
}
function reduceWeightSum(sum, item) {
return sum + getWeightByType(item);
}
function getCollectionWeight(collection) {
return getCollectionValues(collection)
.reduce(reduceWeightSum, 0);
}
let collectionValues;
if (collection instanceof Array) {
collectionValues = collection;
} else if (collection instanceof Map) {
collectionValues = getMapValues(collection);
} else {
collectionValues = getPlainObjectValues(collection);
}
return collectionValues.reduce(function(sum, item) {
return sum + getWeightByType(item);
}, 0);
function getCollectionWeight(collection) {
let collectionValues;
if (collection instanceof Array) {
collectionValues = collection;
} else if (collection instanceof Map) {
collectionValues = [...collection.values()];
} else {
collectionValues = Object.keys(collection)
.map(function (key) {
return collection[key];
});
}
return collectionValues
.reduce(function(sum, item) {
if (item == null) {
return sum + 1;
}
if (typeof item === 'object' ||
typeof item === 'function') {
return sum + 4;
}
return sum + 2;
}, 0);
}
function getWeightByType(value) {
const WEIGHT_NULL_UNDEFINED = 1;
const WEIGHT_PRIMITIVE = 2;
const WEIGHT_OBJECT_FUNCTION = 4;
if (value == null) {
return WEIGHT_NULL_UNDEFINED;
}
if (typeof value === 'object' ||
typeof value === 'function') {
return WEIGHT_OBJECT_FUNCTION;
}
return WEIGHT_PRIMITIVE;
}
function getMapValues(map) {
return [...map.values()];
}
function getPlainObjectValues(object) {
return Object.keys(object)
.map(function (key) {
return object[key];
});
}
function getCollectionValues(collection) {
if (collection instanceof Array) {
return collection;
}
if (collection instanceof Map) {
return getMapValues(collection);
}
return getPlainObjectValues(collection);
}
function reduceWeightSum(sum, item) {
return sum + getWeightByType(item);
}
function getCollectionWeight(collection) {
return getCollectionValues(collection)
.reduce(reduceWeightSum, 0);
}
- 可读的getCollectionWeight()函数增加了自解释的代码。
- getCollectionWeight()的体积减少
- getCollectionWeight()函数现在不随判定值的添加的增加而增加
- 分离出来的代码具有低耦合和可复用性了,你的同事也可以将其运用到这些不错的函数在其他项目中,并且是轻而易举的
- 如果一旦代码有报错,报错堆栈更清晰,因为他包含了函数名称,你可以轻易的判定出错的部分
- 分割出来的代码可测试性更高,可以达到更高的测试覆盖率,测试巨大的函数是很糟糕的,前者你可以为每一个函数构造测试和验证它
- 你可以按照commmonjs和ES6去构造你的函数,将其分割成小函数,使你的项目都由小函数构造而成
- The readability of getCollectionWeight() increased by self-explanatory code
- The size of getCollectionWeight() reduced considerable
- getCollectionWeight() function is now protected from fast growth if you plan to implement the weight calculation of other collection types
- The extracted functions are now decoupled and reusable components. Your colleague may ask you to import these nice functions into another project: and you can easily do that
- If accidentally a function generates an error, the call stack will be more precisebecause it contains the function names. Almost instantly you could determine the function that makes problems
- The split functions are much easier to test and reach a high level of code coverage. Instead of testing one big function with all possible scenarios, you can structure your tests and verify each small function separately
- You can benefit from CommonJS or ES2015 modules format. Create from extracted functions separated modules. This makes your project files lightweight and structured.
filter-nav.js
enterOneFilter()
enterTwoFilter()
enterThreeFilter()
......
map.js
createPoint()
setValObj.attr({'businessId':business_id});
setValObj.attr({'share-url':shareUrl});
setValObj.attr({'share-pic':sharePic});
setValObj.attr({'share-con':shareContent});
setValObj.attr(
{'businessId':business_id},
{'share-url':shareUrl},
{'share-pic':sharePic},
{'share-con':shareContent}
);
judgeUrlOut:function(type,currPoint,oldImgName){
if(type == "service"){
if(oldImgName == 'servicediamond'){
currPoint.attr('src', ZBJMap.mapUrl.serviceDiamond);
} else if(oldImgName == 'servicenormal'){
currPoint.attr('src', ZBJMap.mapUrl.serviceNormal);
} else if(oldImgName == 'servicecrown'){
currPoint.attr('src', ZBJMap.mapUrl.serviceCrown);
} else if(oldImgName == 'servicet'){
currPoint.attr('src', ZBJMap.mapUrl.serviceT);
}
} else if(type == "business"){
if(oldImgName == 'businessdiamond'){
currPoint.attr('src', ZBJMap.mapUrl.businessDiamond);
} else if(oldImgName == 'businessnormal'){
currPoint.attr('src', ZBJMap.mapUrl.businessNormal);
} else if(oldImgName == 'businesscrown'){
currPoint.attr('src', ZBJMap.mapUrl.businessCrown);
} else if(oldImgName == 'businesst'){
currPoint.attr('src', ZBJMap.mapUrl.businessT);
}
}
},
switch case
switch只计算一次值 然后都是test,jmp, if...else 是每个条件都要计算一遍的.
Write small and plain functions
By pokerone
Write small and plain functions
- 849