---
# System prepended metadata

title: DRY 第一哩路 - 抽象化
tags: [Functional Programming]

---

---
tags: Functional Programming
---
# DRY 第一哩路 - 抽象化

當我們把一段程式碼包成一個 function 時，其實就是在作抽象化，良好的抽象化可以讓 function 更有彈性，也更容易維護、使用，假設現在有一個需要過濾物件的 function：

```jsx
//資料
var taskArray = [
  {
    id: 1,
    userId: 1,
    userName: 'Jerry',
    complete: false,
    title: 'Write 《Think in FP》 serise article',
    content: '...',
    dueDate: '2020-05-31',
    priority: 0,
  },
  ...
];
```

如果需求是想過濾出已經完成的資料 `complete === true` 

```jsx
//過濾 function
function filterTaskArray(taskArray) {
  const result = [];
  for (let i = 0; i < taskArray.length; i++) {
    if (taskArray[i].complete) {
      result.push(taskArray[i]);
    }
  }
  return result;
}
```

這時 PM 又想新增一個過濾出某個特定的 userId，有可能就會變成下面這樣：

```jsx
function filterTaskArray(taskArray, { filterComplete = false, filterUserId = undefined } = {}) {
  const result = [];
  for (let i = 0; i < taskArray.length; i++) {
    if (
      (filterComplete && !taskArray[i].complete) ||
      (filterUserId != null && taskArray[i].userId !== filterUserId)
    ) {
      continue;
    }
    result.push(taskArray[i]);
  }
  return result;
}
```

使用方式變成這樣：

```jsx
const result = filterTaskArray(taskArray, {
  filterComplete: true,
  filterUserId: 1
});
```

可以知道，這樣的抽象是非常糟糕的，每次新增一個需求就需要多幾個條件，使用者會需要填入更多的參數，整個功能也會非常難讀懂。

所以當一個 function 可以過濾出一個 array 某些特定的元素時，需要抽象的事情只有一個 -- 過濾 Array，至於是過濾什麼條件就應該由外部的使用者決定！

這樣一來，就等於把部分邏輯交由外部的使用者決定，我們要做的只需要規定使用者傳一個 function 進來，並預期這個 function 會回傳某種值就可以了

```jsx
function filter(array, fn) { // function 改名為 filter，參數改名為 array
  const result = [];
  for (let i = 0; i < array.length; i++) {
    if (fn(array[i])) { // 傳 array[i] 進去，並預期 fn(array[i]) 會回傳 Boolean。
      result.push(array[i]);
    }
  }
  return result;
}
```

使用方式就變成下面這樣：

```jsx
const result = filter(taskArray, item => item.complete && item.userId = 1)
```

## 總結

抽象做得好，其實彈性是非常大的，只要是 array 都可以使用，並且邏輯可以交由使用的人依據情況決定，也幾乎不再需要再修改這個 function！