函数式编程初学者的 3 条代码整洁原则
1. 给事物起一个有意义的名字
当你给变量命名时,你给它起的名字必须能告诉我们这个变量的整个生命周期。它必须告诉我们这个变量是谁,以及它为什么存在。变量的名称是它的卖点,因此必须对其进行恰当的描述。
它应该告诉你它存在的原因、它的作用以及如何使用。如果一个名称需要注释,那么这个名称就没有意义。
考虑变量
const p = [] //list of pets
P 可以是任何值,当在复杂循环或函数中使用此变量时,这会使其更难读取。
这是一个更有意义的名字
const bertsPetList = []
因为它告诉你
什么? 伯特的宠物列表
为什么? 用于对伯特和他的宠物感兴趣的代码操作
怎么做? 作为标准 js 数组
1.2 函数名必须体现意图
当命名一个函数时,我们还必须考虑“什么?”,“为什么?”和“如何?”
它起什么作用?
为什么这样做?
它是如何做到的?
举个例子,你想要获得特定主人可能拥有的动物列表,这些动物被允许与主人一起待在家里。
const bertsPets = [
{
name: "Snizzles"
type: "nope"
lives: "outdoors"
},
{
name: "Terrance"
type: "danger-woof"
lives: "outdoors"
},
{
name: "Kevin"
type: "doggo"
lives: "indoors"
}
]
例如,这种函数的名称可能是findPets
,尽管名称有意义,但对于下一个阅读代码的程序员来说,它不足以轻松理解正在发生的事情。
所以也许你可以尝试一下这个名字findPetsThatLiveIndoors
这很好,但就 DRY 而言(我们将在下一节讨论这个问题),你的代码是有害的,因为对于每个生活区类型,你都必须创建一个与该类型相对应的函数,
即
const findPetsThatLiveIndoors = () => {}
const findPetsThatLiveOutdoors = () => {}
const findPetsThatLiveInOtherPlace1= () => {}
const findPetsThatLiveInOtherPlace2 = () => {}
从而不必要地重复自己。(这很糟糕)
那么我们可以给我们的函数起什么名字呢?
const filterPetsByLivingAreaInList = () => {}
// which could then be
const filterPetsByLivingAreaInList = (area, list) => list.filter(pet => pet.lives === area)
// and can produce
const bertsIndoorPets = filterPetsByLivingAreaInList('indoors',bertsPets)
现在这个名字告诉我们
什么? 生活在特定区域的宠物
如何? 通过过滤列表
为什么? 获取特定主人可能拥有并允许其在家中生活的动物列表
2.不要重复自己
DRY 原则简单地意味着您不应该有代码重复。
2.1 变量作用域
当可以使用全局作用域时,不要为每个函数作用域重新创建变量,
例如
const getDoggosThatLiveIndoors = () => {
const doggos = getPetsByType('doggo', bertsPets);
const doggosThatLiveIndoors = filterPetsByLivingAreaInList('indoors', doggos);
return doggosThatLiveIndoors;
}
const getDoggosThatLiveOutdoors= () => {
const doggos = getPetsByType('doggo', bertsPets);
const doggosThatLiveIndoors = filterPetsByLivingAreaInList('indoors', doggos);
return doggosThatLiveOutdoors;
}
console.log(`${getDoggosThatLiveIndoors().length} doggos live indoors`)
console.log(`${getDoggosThatLiveOutdoors().length} doggos live outdoors`)
在上面的例子中,变量 doggos 可以在全局范围内定义,以避免为每个函数重新定义它
const doggos = getPetsByType('doggo', bertsPets);
const getDoggosThatLiveIndoors = () => {
const doggosThatLiveIndoors = filterPetsByLivingAreaInList('indoors', doggos);
return doggosThatLiveIndoors;
}
const getDoggosThatLiveOutdoors = () => {
const doggosThatLiveIndoors = filterPetsByLivingAreaInList('outdoors', doggos);
return doggosThatLiveOutdoors;
}
console.log(`${getDoggosThatLiveIndoors().length} doggos live indoors`)
console.log(`${getDoggosThatLiveOutdoors().length} doggos live outdoors`)
2.2 函数操作
在上面的例子中,两个函数getDoggosThatLiveIndoors
和getDoggosThatLiveOutdoors
执行相同的操作,因此可以优化为一个
const doggos = getPetsByType('doggo', bertsPets);
const getDoggosByLivingArea = (areaType) => {
const doggosInArea = filterPetsByLivingAreaInList(areaType, doggos);
return doggosInArea;
}
const areaTypes = ['indoors', 'outdoors'];
areaTypes.map( type =>
console.log(`${getDoggosByLivingArea(type).length} doggos live ${type}`)
)
重复可能是软件中所有问题的根源。为了控制或消除重复,人们创建了许多原则和实践。
3. 函数应该只做一件事
在创建函数时,我们应该确保它们只实现一个定义的目标
现在想象以下函数
const favoritePets = ['cat', 'doggo']
const getFavoritePets = (favoritePets, petList) => {
const ownerHasCats = hasPetType('cats', petList);
if(!ownerHasCats){
const cats = [cat1, cat2, cat3]
const petsWithCats = insertPets(cats, petList)
return filterPets(favoritePets, petsWithCats )
}
return filterPets(favoritePets, petList )
}
这个函数应该只用来获取主人最喜欢的宠物,但它却试图查询主人的猫是否已添加到宠物列表中,如果不可用就添加。这违反了单一职责原则,因为这个函数做了太多事情。它有很多职责。它的名字就getFavoritePets
不合适getFavoritePetsAndCheckIfOwnerHasCatsIfNotAddCatsToTheOwnersPetList
😂
更好的方法是
const cats = [cat1, cat2, cat3]
const bertsPetsWithCats = insertPets(cats, bertsPets)
const favoritePets = ['cat', 'doggo']
const getFavoritePets = (favoritePetTypes, petList) => filterPets(favoritePetTypes, petList);
const bertsFavoritePets = getFavoritePets(favoritePets, bertsPetsWithCats);
回顾
为了在函数式编程范式中编写干净的代码,我们必须遵循 3 个基本原则。
- 给事物起有意义的名字
- 不要重复自己
- 函数应该只做一件事
为了更深入地了解清洁代码,我建议您阅读清洁代码手册
大功告成 :)
这是一个代码土豆
