如何写出不可替代的代码?
前言
本文是学习《重构:改善既有代码的设计》后的一些心得,希望能用趣味的方式结合一些实例带领大家一起学习,提升自身代码质量。
想必最近的互联网裁员消息大家也有所耳闻,那么我们怎么才能够在这样的大环境下苟住自身呢?经过我的总结,我认为大家都不具备不可替代性。
什么叫不可替代性呢,通俗点来说就是,这活除了你别人都干不了。达到这种境界无异于两种情况,一种是自身过于优秀,优秀到没人能取代(该情况过少,希望大家能正视己身)。
另一种方法则是,制作出专属于你的代码!!下面我们来一起学习,怎样写出专属于你,不可被替代的代码!
以下不可替代写法皆为反面教材!!!
一、神秘命名(Mysterious Name)
命名让人猜不透,摸不准!
不可替代写法:
const getPNum = (number) => {
......
}
无论是函数命名还是入参命名,相信都很难有人能参透你的深意,在别人接手你的代码时,必定会来向你请教,这在老板眼里你的价值将更为突出。
正常写法:
const getPhoneCode = (phoneNumber) => {
......
}
从函数的驼峰命名我们可以很轻易猜出是获取手机验证码,入参也能猜出是手机号码的意思,这样的代码太通俗易懂了,显然达不到我们的效果。
二、重复代码(Duplicated Code)&& 过长函数(Long Function)
重复编写大量相同代码,内容过多的函数,使代码变得臃肿难以维护
不可替代写法:
const showUserInfo = () => {
let totalAmount = 0;
const userInfo = request.get('/userInfo', 'admin')
const userAmountList = request.get('/userAmountList', 'admin')
console.log('name', userInfo.name);
console.log('age', userInfo.age);
console.log('sex', userInfo.sex);
console.log('address', userInfo.address);
for(let i of userAmountList) {
totalAmount += i.amount
}
console.log('总金额', totalAmount);
}
大量重复的代码让人瞬间产生疲劳感,完全不搭边的代码顺序混淆人的双眼,如果再加上一些神秘命名,必将让代码更上一个台阶。
正常写法:
const showUserInfo = () => {
const printUserDetail = (userInfo) => {
const { name, age, sex, address } = userInfo;
console.log('name', name);
console.log('age', age);
console.log('sex', sex);
console.log('address', address);
const printTotalAmount = (userAmountList) => {
const totalAmount = userList.reduce((pre, cur) => pre + cur.amount, 0)
console.log('总金额', totalAmount);
}
// 获取用户信息
const userInfo = request.get('/userInfo', 'admin')
printUserDetail(userInfo)
// 获取用户金额列表
const userAmountList = request.get('/userAmountList', 'admin')
printTotalAmount(userAmountList)
}
重复代码都被提炼到单独的函数模块中,用reduce免去了重复的代码相加,并且代码顺序也被移动至有关联的地方,这样的代码换做刚学前端的小白恐怕也能看懂,这样明显不能凸显自身的独特。
三、过长参数列表(Long Parameter List)
函数或组件的参数过多,影响代码可读性,某些环境甚至会对性能造成影响
不可替代写法:
const getList = (id, name, age, address, sex) => {
...
}
正常写法:
const getList = (data) => {
const { id, name, age, address, sex } = data;
...
}
将入参放置到一个对象中,再到函数里通过解构的方式进行调用。这样的方式太过简洁,过少的入参凸显不出你这个函数的重要性。
四、全局数据
将数据全部挂载到全局,导致内存不及时被释放以及全局污染
不可替代写法:
const id = 1;
const data1 = request.get('/userInfo', id)
const data2 = request.get('/userState', id)
const getUserInfo = () => {
...
}
const getUserState = () => {
...
}
所有变量放入全局,把后续开发者的路变窄,不敢随意去更改变量,此刻再加上神秘命名,相信没有人能够取代你的位置。
正常写法:
const id = 1;
const getUserInfo = () => {
const data = request.get('/userInfo', id)
...
}
const getUserState = () => {
const data = request.get('/userState', id)
...
}
id作为多处用到变量,写到全局,剩下的局部变量都写在各自函数中,即不会引起全局污染,也不会担心命名重复的问题。在各自的作用域中作用也清晰明了。
五、发散式变化(Divergent Change)
将需要做的事分散到各个地方,每次修改需要修改对应函数,修改不当会导致另一个依赖此函数的功能崩塌
不可替代写法:
const getPrice = (list) => {
const printName = (item) => {
if(item.type === 'totalList') {
console.log('totalName', item.name);
}else if(item.type === 'frozenList'){
console.log('frozenName', item.name);
}
}
const calcPrice = (item) => {
if(item.type === 'totalList') {
// todo: 计算totalPrice
const price = ...;
return price;
}else if(item.type === 'frozenList'){
// todo: 计算frozenPrice
const price = ...;
return price;
}
}
printName(list.totalList);
printName(list.frozenList);
return calcPrice(list.totalList) - calcPrice(list.frozenList)
}
将方法写成公用方法,在每次修改或者新增时候都需要去修改对应的方法。无法知道每个价格对应着哪些操作,当增加一个新的价格类型时,需要同时去多个函数中添加对应的判断逻辑。一不注意就会忘加漏加形成bug。测试绩效max!
正常写法:
const getPrice = (list) => {
const totalPrice = (item) => {
// todo: 计算totalPrice
const price = ...
console.log('totalName', item.name);
console.log('price', price);
}
const frozenPrice = (item) => {
// todo: 计算frozenPrice
const price = ...
console.log('frozenName', item.name);
console.log('price', price);
}
return totalPrice(list.totalList) - frozenPrice(list.frozenList)
}
每个价格对应需要的操作都被提炼到单独的函数,专注于负责自己的事,如果价格计算方式需要改变,可以更加直观的修改。若需要添加新的价格品种,也将会更好添加。
六、霰弹式修改(Shotgun Surgery)
多处共用一个属性,不设置全局变量管理,每次修改需要修改大量代码
不可替代写法:
getList(globalModel.id)
getUserInfo(globalModel.id)
getUserAmount(globalModel.id)
当需求改变,需要在多处进行修改,这样的工作量倍增,会让你的工作力max!
正常写法:
const id = globalModel.id;
getList(id)
getUserInfo(id)
getUserAmount(id)
同一个属性被多处使用,使用一个变量进行存储,当需求发生改变(例如globalModel.id变为globalModel.userId),只需要修改一处便能完成,大大节省时间。
七、依恋情结(Feature Envy)
大量引入其他函数或模块方法,导致代码耦合度极高,动一处则牵扯全身
不可替代写法:
class Price {
constructor() {}
add(...num) {
return num.reduce((pre, cur) => pre + cur, 0);
}
dataFilter(value) {
return parseInt(value.substring(0, value.length - 2)) * 100;
}
}
class Amount {
constructor() {}
getAmount(amountList) {
const _amountList = amountList.map(item => {
return new Price().dataFilter(item);
});
return new Price().add(..._amountList);
}
}
所有的计算函数全部使用其他类里的方法,形成大量依赖。你要出事我跟着一起死,我就是要用你的,我就是玩~
正常写法:
class Amount {
constructor() {}
add(...num) {
return num.reduce((pre, cur) => pre + cur, 0);
}
dataFilter(value) {
return parseInt(value.substring(0, value.length - 2)) * 100;
}
getAmount(amountList) {
const _amountList = amountList.map(item => {
return this.dataFilter(item);
});
return this.add(..._amountList);
}
}
类里所有使用的方法都在本身完成,所有的问题都在自身解决,形成闭环。
八、数据泥团(Data Clumps)
众多数据糅合在一起,当其中某一项数据失去意义时,其他项数据也失去意义。
不可替代写法:
const lastName = "卢"
const firstName = "本伟"
const name = `${lastName}${firstName}`
发现当其中某个变量失去意义的时候,另一个变量也失去意义,一损俱损。
正常写法:
const person = {
lastName: "卢",
firstName: "本伟"
}
const name = `${person.lastName}${person.firstName}`
有强联系的数据,应为它们产生一个新对象。
九、基本类型偏执(Primitive Obsession)
认为基本类型一定更加简单,偏执的使用大量的基本类型而不去定义应有的结构
不可替代写法:
class Price {
constructor(name, money) {
this.name = name;
this.money = money;
}
get name() {
return name;
}
get count() {
return parseFloat(this.money.slice(1));
}
get current() {
return this.money.slice(0, 1);
}
get unit() {
switch (this.money.slice(0, 1)) {
case '¥':
return 'CNY';
case '$':
return 'USD';
case 'k':
return 'HKD';
}
}
calcPrice() {
// todo: 金额换算
}
}
const myPrice = new Price("罐头", "$30")
偏执地使用字符串基本类型定义money,表面是Price的类,但在里面充斥着大量的money的数据处理。
正常写法:
class Money {
constructor(value) {
this.value = value;
}
get count() {
return parseFloat(this.value.slice(1));
}
get current() {
return this.value.slice(0, 1);
}
get unit() {
switch (this.value.slice(0, 1)) {
case '¥':
return 'CNY';
case '$':
return 'USD';
case 'k':
return 'HKD';
}
}
}
class Price {
constructor(name, money) {
this.name = name;
this.money = new Money(money);
}
get name() {
return name;
}
calcPrice() {
// todo: 金额换算
}
}
const myPrice = new Price("罐头", "$20")
money中存在着大量的数据处理,应为期单独建立个对象来作为它的类型。
十、重复的switch(Repeated Switches)
大量使用重复逻辑的switch,这样的代码是臃肿且脆弱的。
不可替代写法:
class Money {
constructor(value) {
this.value = value;
}
get count() {
return parseFloat(this.value.slice(1));
}
get current() {
return this.value.slice(0, 1);
}
get unit() {
switch (this.current) {
case '¥':
return 'CNY';
case '$':
return 'USD';
case 'k':
return 'HKD';
}
}
get suffix() {
switch (this.current) {
case '¥':
return '元';
case '$':
return '美元';
case 'k':
return '港币';
}
}
get currentCount() {
switch (this.current) {
case '¥':
return this.count;
case '$':
return this.count * 7;
case 'k':
return this.count * 0.8;
}
}
}
大量相同逻辑的switch,若想增加一个判断项,需找到所有switch项进行修改,一不注意则会遗漏,引发bug
正常写法:
class Money {
constructor(value) {
this.value = value;
}
get count() {
return parseFloat(this.value.slice(1));
}
get current() {
return this.value.slice(0, 1);
}
}
class cnyMoney extends Money {
constructor(props) {
super(props);
}
get unit() {
return 'CNY';
}
get suffix() {
return '元';
}
get currentCount() {
return this.count;
}
}
class usdMoney extends Money {
constructor(props) {
super(props);
}
get unit() {
return 'USD';
}
get suffix() {
return '美元';
}
get currentCount() {
return this.count * 7;
}
}
class hkdMoney extends Money {
constructor(props) {
super(props);
}
get unit() {
return 'HKD';
}
get suffix() {
return '港币';
}
get currentCount() {
return this.count * 0.8;
}
}
每一个分支项专注于自身的变化,修改时不会担心某处遗漏。
原文地址: https://juejin.cn/post/7126888773647876110#heading-