浅谈JavaScript 代码整洁之道(10)

好:

class Shape {
 constructor() {}

 setColor(color) {
  // ...
 }

 render(area) {
  // ...
 }
}

class Rectangle extends Shape {
 constructor() {
  super();
  this.width = 0;
  this.height = 0;
 }

 setWidth(width) {
  this.width = width;
 }

 setHeight(height) {
  this.height = height;
 }

 getArea() {
  return this.width * this.height;
 }
}

class Square extends Shape {
 constructor() {
  super();
  this.length = 0;
 }

 setLength(length) {
  this.length = length;
 }

 getArea() {
  return this.length * this.length;
 }
}

function renderLargeShapes(shapes) {
 shapes.forEach((shape) => {
  switch (shape.constructor.name) {
   case 'Square':
    shape.setLength(5);
   case 'Rectangle':
    shape.setWidth(4);
    shape.setHeight(5);
  }

  let area = shape.getArea();
  shape.render(area);
 })
}

let shapes = [new Rectangle(), new Rectangle(), new Square()];
renderLargeShapes(shapes);

接口隔离原则(ISP)

JavaScript 中没有接口,所以实行这个原则不能像其它语言那样严格。然而即使对 JavaScript 的弱类型系统来说,它仍然是重要的相关。

ISP 指出,“客户不应该依赖于那些他们不使用的接口。” 由于 Duck Typing 理论,接口在 JavaScript 中是个隐性契约。

在 JavaScript 中有一个很好的例子来演示这个原则,即一个拥有巨大设置对象的类。比较好的做法是不要求客户设置大量的选项,因为多数时候他们不需要所有设置。让这些选项成为可选的有助于防止“胖接口”。

不好:

class DOMTraverser {
 constructor(settings) {
  this.settings = settings;
  this.setup();
 }

 setup() {
  this.rootNode = this.settings.rootNode;
  this.animationModule.setup();
 }

 traverse() {
  // ...
 }
}

let $ = new DOMTraverser({
 rootNode: document.getElementsByTagName('body'),
 animationModule: function() {} // 多数时候我们不需要动画
 // ...
});

好:

class DOMTraverser {
 constructor(settings) {
  this.settings = settings;
  this.options = settings.options;
  this.setup();
 }

 setup() {
  this.rootNode = this.settings.rootNode;
  this.setupOptions();
 }

 setupOptions() {
  if (this.options.animationModule) {
   // ...
  }
 }

 traverse() {
  // ...
 }
}

let $ = new DOMTraverser({
 rootNode: document.getElementsByTagName('body'),
 options: {
  animationModule: function() {}
 }
});

依赖倒置原则(DIP)

这个原则说明了两个基本问题:

1. 上层模块不应该依赖下层模块,两者都应该依赖抽象。

2. 抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

这一开始可能很难理解,但是如果你使用 Angular.js,你已经看到了对这个原则的一种实现形式:依赖注入(DI)。虽然它们不是完全相同的概念,DIP 阻止上层模块去了解下层模块的细节并设置它们。它可以通过 DI 来实现。这带来的巨大好处降低了模块间的耦合。耦合是种非常不好的开发模式,因为它让代码难以重构。