---
# System prepended metadata

title: 'CH12: Modular design pattern, p.336-346'
tags: [clean code]

---

# CH12: Modular design pattern, p.336-346
###### tags: `clean code`
## 模組化設計模式 Modular design pattern
> 常用來製作單一模組的語法和結構慣例。

使用時機：
- 這個概念可以表達為名詞嗎?
- 這個概念是否需要建構?
- 實例之間的概念會有所不同嗎?
:::danger
JavaScript 終究是原型（Prototype-Based）的語言，并非物件導向（OOP）語言！
:::

## Before ES6 Constructor Function
> 很直白的使用 prototype 建立類 OOP 類別
```javascript=
function Book(title){
    this.title = title;
    // ...
}
```
### 將各方法指定給其 prototype
```javascript=
// way01 方法定義（相對 way02 省空間）
Book.prototype.getBookName = function(){ return this.title };
// [X] Book.prototype.getBookName = () => this.title 不可以使用箭頭函式哦

// way02 物件字面值（更封裝簡潔）
Book.prototype = {
    getBookName: function(){ /**/ };
    getNumberOfPage: function(){ /**/ };
    // ...
}
```
### 實現繼承

```javascript=
// Object.prototype
//     -> Animal.protype
//        -> Monkey.prototype

function Animal(){}
Animal.prototype = {
        isAnimal: true,
        grow(){}
}
```
- 創建子物件&新增屬性方法
```javascript=
// way01 使用 ES5 Object.create()
function Monkey(){}
Monkey.prototype = Object.create(Animal.protoype);
Monkey.prototype.isMonkey = true; 
Monkey.prototype.method = function(){};


// way02 使用 new 實體化
const Monkey = new Animal;
Monkey.isMonkey = true;
```
- 創建子物件+新增屬性方法(一次解決)
```javascript=
// 使用 Object.assign()
Monkey.prototype = Object.assign(Object.create(Animal.protoype),{
    isMonkey = true,
    screen(){/**/},
    // ...
})
```
## ES6 Class
> 長得更像經典 OOP 語言，且語法更為簡潔。（但充其量也只是語法糖）

![](https://i.imgur.com/gBWIQwD.png)

```javascript=
class Name{
    // 必須有（且唯一）一個 constructor 方法（如果沒有定義會默認添加）
    constructor(forename,surname){
        this.forename = forename;
        this.surname = surname; 
    }
    sayHello(){
        return `My name is ${this.forename} ${this.surname}`
    }
}
```
### **定義靜態方法**
> 靜態方法只能由類別本身呼叫，由類別所建立的物件實體（instance）上不能被呼叫。
通常作為工具函式（utility functions）使用。


:::spoiler **static 關鍵字** [MDN](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Classes/static)

```javascript=
class Triple {
  static triple(n) {
    n = n || 1; //should not be a bitwise operation
    return n * 3;
  }
}

class BiggerTriple extends Triple {
  static triple(n) {
    return super.triple(n) * super.triple(n);
  }
}

console.log(Triple.triple());        // 3
console.log(Triple.triple(6));       // 18
console.log(BiggerTriple.triple(3)); // 81
var tp = new Triple();
console.log(tp.triple()); 
```
:::

```javascript=
class Account{
    static allAccounts = [];
    // ...
}
```
### 定義私有欄位 Private Field
> 私有欄位只能由類別本身進行存取，子類別則無權存取。
:::danger
使用私有欄位時需格外謹慎，因爲它會嚴重限制程式碼的可擴展性。
:::
```javascript=
class Rectangle{
    name = "rectangle";  // 一般公有欄位
    #width = 200;  // 使用 # 符號定義私有欄位
    #height = 100;
    constructor(width, height){
        this.#width = width;  // 私有欄位是可以在内部輕易覆蓋的
        this.#height = height;
    }
}
```
:::success
**使用 _ 標記私有欄位（僞私有欄位 pseudo）**
傳統 JavaScript 沒有私有欄位的概念，通常程式設計師會選擇使用一個或多個「底線」置於私有屬性之前（__somepropertyname）。此做法為一種社會共識，并非真的能限制屬性的存取！
:::
### 擴展類別 Extends
> 使用 class ... extends 可以簡單的實現擴展(繼承)
```javascript=
class Animal{};
class Tiger extends Animal{};  // 使用 extends 關鍵字
```
### 混合類別 Mixin
:::info
JavaScript 沒有提供原生的混合機制，因此若要實現此目的，你可以在定義之後擴增原型，或者有效繼承自原有的 Mixin（就像他們是父類別一樣）。
:::
#### way01 將 Mixin 指定為物件，然後將他們都增加到類別的 prototype。
```javascript=
const fooMixin = {foo(){}};
const bazMixin = {baz(){}};

class MyClass {}
Object.assign(MyClass.prototype, fooMixin, bazMixin);
```
:::danger
BUT! 這種方法不允許覆蓋自己的 Mixin 方法
```javascript=
class MyClass { foo(){} }
Object.assign(MyClass.prototype, fooMixin, bazMixin);

// fooMinxin.foo 覆蓋了原本 MyClass 的 foo 方法
new MyClass().foo = fooMinxin.foo()  // true
```
:::
#### way02 使用繼承（**子類別工廠 Subclass Factory**）
```javascript=
const fooSubclassFactory = SuperClass => {
    return class extends SuperClass{
        fooMethod1(){};
        fooMethod2(){};
    }
}
```
### 存取父類別
> 使用 class 定義的類別中的所有函數，都具有可用的 super 綁定，能提供對父類別及其屬性的存。

#### [super 關鍵字用法](https://developer.mozilla.org/zh-W/docs/Web/JavaScript/Reference/Operators/super)
1. **當作函式** | 將會呼叫父類別的建構函式（需要放在 constructor 裏面） -> `super()`
2. **當作物件** | 提供對特定方法（supper.methodName()）的存取  -> super.xx
```javascript=
class Tiger extends Animal {
    constructor(){
        super()  // 呼叫 Animal constructor
    }
}
```
:::warning
:bomb: **若要擴展另一個類別並額外定義新的屬性，就必須叫用 super()**。
子類別的 constructor 呼叫 super() 之前，this 是沒有指向的，會跑出Refference Error！！！
```javascript=
class Tiger extends Animal {
    constructor(){
        this.someProperty = 123;  // ReferenceError！
        super() 
    }
}
```
:::
