如何写出不可替代的代码?

2022-11-11 16:36:42 浏览数 (1)

前言

本文是学习《重构:改善既有代码的设计》后的一些心得,希望能用趣味的方式结合一些实例带领大家一起学习,提升自身代码质量。

想必最近的互联网裁员消息大家也有所耳闻,那么我们怎么才能够在这样的大环境下苟住自身呢?经过我的总结,我认为大家都不具备不可替代性。

什么叫不可替代性呢,通俗点来说就是,这活除了你别人都干不了。达到这种境界无异于两种情况,一种是自身过于优秀,优秀到没人能取代(该情况过少,希望大家能正视己身)。

另一种方法则是,制作出专属于你的代码!!下面我们来一起学习,怎样写出专属于你,不可被替代的代码!

以下不可替代写法皆为反面教材!!!

一、神秘命名(Mysterious Name)

命名让人猜不透,摸不准!

不可替代写法:

代码语言:javascript复制
const getPNum = (number) => {
  ......
}

无论是函数命名还是入参命名,相信都很难有人能参透你的深意,在别人接手你的代码时,必定会来向你请教,这在老板眼里你的价值将更为突出。

正常写法:

代码语言:javascript复制
const getPhoneCode = (phoneNumber) => {
  ......
}

从函数的驼峰命名我们可以很轻易猜出是获取手机验证码,入参也能猜出是手机号码的意思,这样的代码太通俗易懂了,显然达不到我们的效果。

二、重复代码(Duplicated Code)&& 过长函数(Long Function)

重复编写大量相同代码,内容过多的函数,使代码变得臃肿难以维护

不可替代写法:

代码语言:javascript复制
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);
}

大量重复的代码让人瞬间产生疲劳感,完全不搭边的代码顺序混淆人的双眼,如果再加上一些神秘命名,必将让代码更上一个台阶。

正常写法:

代码语言:javascript复制
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)

函数或组件的参数过多,影响代码可读性,某些环境甚至会对性能造成影响

不可替代写法:

代码语言:javascript复制
const getList = (id, name, age, address, sex) => {
  ...
}

正常写法:

代码语言:javascript复制
const getList = (data) => {
  const { id, name, age, address, sex } = data;
  ...
}

将入参放置到一个对象中,再到函数里通过解构的方式进行调用。这样的方式太过简洁,过少的入参凸显不出你这个函数的重要性。

四、全局数据

将数据全部挂载到全局,导致内存不及时被释放以及全局污染

不可替代写法:

代码语言:javascript复制
const id = 1;
const data1 = request.get('/userInfo', id)
const data2 = request.get('/userState', id)

const getUserInfo = () => {
  ...
}

const getUserState = () => {
  ...
}

所有变量放入全局,把后续开发者的路变窄,不敢随意去更改变量,此刻再加上神秘命名,相信没有人能够取代你的位置。

正常写法:

代码语言:javascript复制
const id = 1;

const getUserInfo = () => {
  const data = request.get('/userInfo', id)
  ...
}

const getUserState = () => {
  const data = request.get('/userState', id)
  ...
}

id作为多处用到变量,写到全局,剩下的局部变量都写在各自函数中,即不会引起全局污染,也不会担心命名重复的问题。在各自的作用域中作用也清晰明了。

五、发散式变化(Divergent Change)

将需要做的事分散到各个地方,每次修改需要修改对应函数,修改不当会导致另一个依赖此函数的功能崩塌

不可替代写法:

代码语言:javascript复制
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!

正常写法:

代码语言:javascript复制
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)

多处共用一个属性,不设置全局变量管理,每次修改需要修改大量代码

不可替代写法:

代码语言:javascript复制
getList(globalModel.id)
getUserInfo(globalModel.id)
getUserAmount(globalModel.id)

当需求改变,需要在多处进行修改,这样的工作量倍增,会让你的工作力max!

正常写法:

代码语言:javascript复制
const id = globalModel.id;
    
getList(id)
getUserInfo(id)
getUserAmount(id)

同一个属性被多处使用,使用一个变量进行存储,当需求发生改变(例如globalModel.id变为globalModel.userId),只需要修改一处便能完成,大大节省时间。

七、依恋情结(Feature Envy)

大量引入其他函数或模块方法,导致代码耦合度极高,动一处则牵扯全身

不可替代写法:

代码语言:javascript复制
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);
  }
}

所有的计算函数全部使用其他类里的方法,形成大量依赖。你要出事我跟着一起死,我就是要用你的,我就是玩~

正常写法:

代码语言:javascript复制
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)

众多数据糅合在一起,当其中某一项数据失去意义时,其他项数据也失去意义。

不可替代写法:

代码语言:javascript复制
const lastName = "卢"
const firstName = "本伟"
const name = `${lastName}${firstName}`

发现当其中某个变量失去意义的时候,另一个变量也失去意义,一损俱损。

正常写法:

代码语言:javascript复制
const person = {
  lastName: "卢",
  firstName: "本伟"
}
const name = `${person.lastName}${person.firstName}`

有强联系的数据,应为它们产生一个新对象。

九、基本类型偏执(Primitive Obsession)

认为基本类型一定更加简单,偏执的使用大量的基本类型而不去定义应有的结构

不可替代写法:

代码语言:javascript复制
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的数据处理。

正常写法:

代码语言:javascript复制
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,这样的代码是臃肿且脆弱的。

不可替代写法:

代码语言:javascript复制
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

正常写法:

代码语言:javascript复制
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-

0 人点赞