【Day 24】用 SOLID 方式开发 React (1)

前言

在 OOP 的世界里,我们常常会听到高内聚(Cohesion),低耦合(Coupling),以及 SOLID 原则,而且 Design Pattern 都是从 SOLID 原则来的,这些名词的目的都是为了让我们的程序码降低坏味道~~但 Javascript 作为一个弱型别的语言,它跟 JAVA, C# 的 SOLID 原则还是有所不同,不过 SOLID 作为开发的通用规则,使用在 Javascript 上还是能有很好的效果

SOLID

单一职责原则(SRP , Single Responsibility Principle)

每个物件的成员最好只负责一件事,不要给予太多的责任

举例而言:
下方程序是在说明,有一个 ScoreCalculate 掌管成绩计算的功能,原始为 100 分,错一次扣 10 分,60分及格

class ScoreCalculate {
  constructor() {
    this.score = 100;
  }
  minus() {
    this.score -= 10;
  }
  checkIfPassTest() {
    if (this.score >= 60) {
      console.log('通过考试');
    } else {
      console.log('没过啊~要重修');
    }
  }
}
const score = new ScoreCalculate;
score.minus();
score.checkIfPassTest();

这原则意味着,如果担的责任越多就越具有高耦合性,以至於後续难以扩展修改,以上面例子来说,很明显 ScoreCalculate已经掌管超出计算范围的功能,还需要另外去判断是否通过考试,因此应该将通过考试这个职责拆分成另外一个单独的 class
举例而言,更改後程序码

class ScoreCalculate {
  constructor() {
    this.score = 100;
  }
  minus() {
    this.score -= 10;
  }
}
class ScorePassAlert {
  checkIfPassTest(s) {
    if (s.score >= 60) {
      console.log('通过考试');
    } else {
      console.log('没过啊~要重修');
    }
  }
}
const s = new ScoreCalculate;
s.minus();
console.log(s.score);
const passOrNot = new ScorePassAlert;
passOrNot.checkIfPassTest(s);

这样的改法,会使得每个元件都可以复用,而且在修改逻辑的时候不影响其他逻辑
而在 React 当中,也有使用 HOC(Higher Order Component) 的高阶做法(後续会单独介绍)

开放封闭原则 (OCP, Open-Close Principle)

物件本身如果已经稳定了,就不应该对已经稳定的程序去做异动

举例而言:
下方程序是在说明,有一个 productAllSort 的 Object 具有获取商品阵列中特定分类的功能(getSort)

let productSorts = ['3C产品', '食品'];
let productAllSort = {
  getSort (label) {
    if (productSorts.indexOf(label) > -1) {
      console.log(`分类: ${label}`)
    } else {
      console.log('不存在')
    }
  }
}
export default productAllSort;

此时,如果需要更动这个模组,添加新的商品种类进去 productSorts 中,让 productAllSort 中获取商品阵列中特定分类的功能(getSort)可以取得,如何在尽量不修改程序逻辑的方式,将此逻辑添加进去?
举例而言,更改後程序码

let productSorts = ['3C产品', '食品'];
let productAllSort = {
  getSort (label) {
    if (productSorts.indexOf(label) > -1) {
      console.log(`分类: ${label}`)
    } else {
      console.log('不存在')
    }
  },
  addSort (label) {
    productSorts.push(label);
  }
}
export default productAllSort;

此时通过添加addSort()这项方法就可以解决这项问题,而且用更改到旧有的程序码,满足了开放封闭的原则。

Liskov替换原则 (LSP,Liskov Substitution Principle)

LSP 原则规范的是继承的关系,子物件必须可以替换父物件,而子物件可以扩展父物件功能,但不会改变父物件原有功能

举例而言:

class Rectangle {
  constructor(width, height) {
    this._width = width;     // _ 在 js 中默认是私有变数,仅存在在 Component 内部
    this._height = height;
  }
  get area() {
    return this._width*this._height;
  }
}
class Square extends Rectangle {
  constructor(width) {
    super(width, width);
  }
}
const rectangle = new Rectangle(3,9);
console.log(rectangle.area);  // 27
const square = new Square(4);
console.log(square.area);  // 16

这里选择用 Rectangle 当作父物件是因为计算面积方法一样(长 * 宽),而 Rectangle 长宽未必相同,因此一定需要两个变数,而 Square 长宽相同,只要覆写父的长和宽的值就行了。
这里通过继承父物件来完成新功能,虽然写起来简单,但是继承後的元件可复用性会比较差,不同於OOP的语言,React 没有像是 interface 的功能,所以像是这种继承父物件的方法有时候子物件反而会多了很多不必要的程序码(继承而来),因此笔者要提醒这种方法要慎用、并且慎选父物件。

在 React 中,大部分是靠父元素传递 props 给子元素,而不是依靠继承关系,所以自然就形成了对於 Component 功能的保护,不容易被修改破坏。例如:<MyButton> 继承於 <Button>这个原生元件,除了去扩展它的样式(像是颜色、点击後的功能等),但并不会破坏掉它是button这件事(代表它还是有button该有的功能),而这就是Liskov替换原则在这里的意义。

结论

  • 初略介绍 SOLID
  • 介绍了 SRP, OCP, LSP

/images/emoticon/emoticon18.gif要开始燃烧了~冲刺


<<:  SDN-Defense #Paper

>>:  Laravel:Route Wildcards 2

h1~h6标题基础用法

<h1> 标题01 </h1> <h2> 标题02 </...

Vue.js 从零开始:Vue CLI 介绍与安装

为什麽要使用Vue CLI 开发环境复杂,透过Vue CLI整合环境,减少整合的时间。 套件使用过多...

iTunes 音乐

假如可以随时随地随心地聆听歌曲就好了!那怎麽将 iTunes 音乐存放在不同的设备中,让它在不同的音...

[Day8] Flutter - 显示文字元件 ( Text )

前言 Hi, 我是鱼板伯爵今天要教大家 Text 这个元件,教学内容只会撷取片段程序码,建议大家搭配...

Day 26-制作购物车之设定Redux: reducers&store

主要呈现实作成果 以下内容有参考教学影片,底下有附网址。 (内容包括我的不专业解说分析及在实作过程中...