Clean Code JavaScript小记(二)

单一职责原则

单一职责原则是面向对象设计原则,为了降低复杂性,耦合性和提高可维护性。一个类只承担一项职责与功能,承担的功能更多会让可读性降低。

实现单一职责的原则

1.识别和分离职责

识别类中是否存在多个功能。

2.分解类

识别出多功能,下一步分解功能到不同的类中。

js 复制代码
// 反例
user类既维护了又用户信息做对用户做了校验。
class User {
  constructor(name, email, password) {
    this.name = name;
    this.email = email;
    this.password = password;
  }
 
  getUserInfo() {
    return { name: this.name, email: this.email };
  }
 
  authenticate(providedPassword) {
    return providedPassword === this.password;
  }
}

//正例
// 分解成2个类  区分用户信息和校验

class UserInfo {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
 
  getUserInfo() {
    return { name: this.name, email: this.email };
  }
}
 
class UserAuthentication {
  constructor(password) {
    this.password = password;
  }
 
  authenticate(providedPassword) {
    return providedPassword === this.password;
  }
}
开/闭原则

代码实体(类,模块,函数等)应该易于扩展,难于修改以适应新的需求

js 复制代码
// 反例
class AjaxRequester {
  constructor() {
    // What if we wanted another HTTP Method, like DELETE? We would have to
    // open this file up and modify this and put it in manually.
    this.HTTP_METHODS = ['POST', 'PUT', 'GET'];
  }

  get(url) {
    // ...
  }

}
// 正例:
class AjaxRequester {
  constructor() {
    this.HTTP_METHODS = ['POST', 'PUT', 'GET'];
  }

  get(url) {
    // ...
  }

  addHTTPMethod(method) {
    this.HTTP_METHODS.push(method);
  }
}

实现类的开闭原则也会涉及到几个关键的设计模式和编程实践,如工厂模式,策略模式组合模式。

里氏替换原则

子类可以扩展父类的功能,但不能改变父类原有的功能。 遵循此原则的编码建议:

  • 子类应通过添加新方法或重写父类抽象方法扩展功能,而非修改父类抽象方法。
  • 参数与返回类型兼容:子类重写父类方法时参数应更宽松(支持多种类型),返回值应更严格。
  • 使用抽象类或接口:通过抽象类或接口定义稳定的部分,将可变的部分封装在具体实现中避免破坏原则。
js 复制代码
示例:
class Animal {
  constructor () {
        
      }
      makeSound(...animals) {
        for(let animal of animals) {
            if (animal instanceof Animal) {
                console.log(animal.name, animal.makeSound())
            } else {
                console.log('拒绝表演,下次别来了')
            }
        }
    }
    
}

class Dog extends Animal{
  constructor (name) {
        this.name=name
      }
      makeSound() {
        console.log("汪汪");
    }

}

class Cat extends Animal{
  constructor (name) {
        this.name=name
      }
      makeSound() {
        console.log("喵");
    }

}
let Animal =new  Animal()
Animal.makeSound(new Dog('小狗'),new Cat('小喵'));

// 这里Animal.makeSound方法不依赖小狗和小喵的实例还是依赖 Animal实例;;小狗/小喵的 makeSound 方法的实现就是里氏替换。
接口隔离原则

在 JS 中,当一个类需要许多参数设置才能生成一个对象时,或许大多时候不需要设置这么多的参数。此时减少对配置参数数量提高系统的灵活性和可维护性。

接口隔离原则的核心思想

  • 单一职责原则:每个接口应该只包含一个客户端(使用者)使用的方法,这样可以避免客户端依赖于不需要的方法。
  • 高内聚、低耦合:通过将接口拆分成更小的部分,可以减少类之间的耦合,提高系统的内聚性。
js 复制代码
// 反例
class MultiFunctionPrinter {
  print() {}
  scan() {}
  fax() {}
  staple() {}
}

// 正例 - 接口隔离
class Printer {
  print() {}
}

class Scanner {
  scan() {}
}

class FaxMachine {
  fax() {}
}

class Stapler {
  staple() {}
}

// 组合功能
class AllInOnePrinter {
  constructor(printer, scanner, faxMachine) {
    this.printer = printer;
    this.scanner = scanner;
    this.faxMachine = faxMachine;
  }

  print() {
    this.printer.print();
  }

  scan() {
    this.scanner.scan();
  }

  fax() {
    this.faxMachine.fax();
  }
}
依赖反转原则

高层模块不应该依赖低层模块 里氏替换实例中 ,Animal.makeSound方法 就是依赖自身的方法。不依赖底层类方法

js 复制代码
 示例
 // 抽象的日志记录器接口
class Logger {
    log(message) {
        throw new Error("Abstract method must be implemented");
    }
}
// 具体的控制台日志记录器实现
class ConsoleLogger extends Logger {
    log(message) {
        console.log(message);
    }
}
// 业务逻辑类,依赖于抽象的日志记录器
class BusinessLogic {
    constructor(logger) {
        this.logger = logger;
    }
    doSomething() {
        let result = "Some operation result";
        this.logger.log(result);
    }
}
// 使用控制台日志记录器注入到业务逻辑类
let business = new BusinessLogic(new ConsoleLogger());
business.doSomething();

使用方法链

js 复制代码
// 反例
class Car {
  constructor() {
    this.make = 'Honda';
    this.model = 'Accord';
    this.color = 'white';
  }

  setMake(make) {
    this.name = name;
  }

  setModel(model) {
    this.model = model;
  }

  setColor(color) {
    this.color = color;
  }

  save() {
    console.log(this.make, this.model, this.color);
  }
}

let car = new Car();
car.setColor('pink');
car.setMake('Ford');
car.setModel('F-150')
car.save();

//正例
class Car {
  constructor() {
    this.make = 'Honda';
    this.model = 'Accord';
    this.color = 'white';
  }

  setMake(make) {
    this.name = name;
    // NOTE: Returning this for chaining
    return this;
  }

  setModel(model) {
    this.model = model;
    // NOTE: Returning this for chaining
    return this;
  }

  setColor(color) {
    this.color = color;
    // NOTE: Returning this for chaining
    return this;
  }

  save() {
    console.log(this.make, this.model, this.color);
  }
}

let car = new Car()
  .setColor('pink')
  .setMake('Ford')
  .setModel('F-150')
  .save();

处理并发

用promise 替代回调

回调不够整洁可能会造成大量嵌套 ES6内置了promise直接使用,promise的使用提高代码整洁性和可维护性

js 复制代码
//反例
require('request').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', function(err, response) {
  if (err) {
    console.error(err);
  }
  else {
    require('fs').writeFile('article.html', response.body, function(err) {
      if (err) {
        console.error(err);
      } else {
        console.log('File written');
      }
    })
  }
})

//正例

require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
  .then(function(response) {
    return require('fs-promise').writeFile('article.html', response);
  })
  .then(function() {
    console.log('File written');
  })
  .catch(function(err) {
    console.error(err);
  })
Async/Await 是较 Promises 更好的选择

promise是相较回调来说是一种更好的选择,ES7中的await和async 更胜于promise

js 复制代码
示例
async function getCleanCodeArticle() {
  try {
    var request = await require('request-promise')
    var response = await request.get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin');
    var fileHandle = await require('fs-promise');

    await fileHandle.writeFile('article.html', response);
    console.log('File written');
  } catch(err) {
    console.log(err);
  }
}

错误处理

使用try/catch 捕获错误并且应当对 catch捕获的报错做出相应的处理方案

js 复制代码
// 反例:
try {
  functionThatMightThrow();
} catch (error) {
  console.log(error);
}
// 正例
try {
  functionThatMightThrow();
} catch (error) {
  // One option (more noisy than console.log):
  console.error(error);
  // Another option:
  notifyUserOfError(error);
  // Another option:
  reportErrorToService(error);
  // OR do all three!
}

promise的catch捕获的报错做出相应的处理方案 与try/catch 同理

格式化

  • 大小写一致,针对团队制定的统一规则保持风格一致.
  • 调用函数和被调用的函数应该放在相近的位置.
相关推荐
ikonan1 小时前
译:Chrome DevTools 实用技巧和窍门清单
前端·javascript
小高0071 小时前
⚡️ Vue 3.5 正式发布:10× 响应式性能、SSR 水合黑科技、告别 .value!
前端·javascript·vue.js
葡萄城技术团队1 小时前
【前沿解析】JavaScript 的未来,将迎来哪些新特性?
javascript
撰卢1 小时前
总结一下vue3的组件之间数据转递,子组件传父组件,父组件传子组件
前端·javascript·vue.js
前端开发爱好者2 小时前
Vue3 超强“积木”组件!5 分钟搞定可交互 3D 机房蓝图!
前端·javascript·vue.js
前端开发爱好者2 小时前
尤雨溪力荐!Vue3 专属!100+ 动效组件!
前端·javascript·vue.js
前端开发爱好者2 小时前
尤雨溪力荐!Vue3 生态最强大的 14 个 UI 组件库!
前端·javascript·vue.js
lb29172 小时前
关于多个el-input的自动聚焦,每输入完一个el-input,自动聚焦到下一个
前端·javascript·vue.js
lingliang2 小时前
使用 JS 渲染页面并导出为PDF 常见问题与修复
javascript·pdf·vue
sorryhc3 小时前
【AI解读源码系列】ant design mobile——Divider分割线
前端·javascript·react.js