はじめに
下記のコードは、公式ドキュメントのコピペです。テクニックについてとても勉強になったので感想を入れつつ、メモを残しています。
Switchによる問題点と関数型プログラミングのアプローチ
Switchによる制御は、分岐が増えることによって制御を行うプログラムの実装が複雑になっていく可能性がある。
例えばReduxにおける、Reducerの実装がそれにあたる。
```js:今後ActionTypeがどんどん増えていくと、todos
の実装は長くなり、Switch分の中身が複雑になっていく
export function todos(state = [], action) {
switch (action.type) {
case ActionTypes.ADD_TODO:
let text = action.text.trim()
return [ ...state, text ]
default:
return state
}
}
関数型プログラミング脳では、このような問題に関して、扱う範囲を分解することでそれぞれをシンプル作れないか?という疑問をいつも持つことが推奨されている。
そこで今回のケースでは、**ある特定の条件に基づいて、それぞれの場合に応じた具体的な処理を決定し実行する**という問題を、
* 任意の条件にマッチしたら対応する関数を呼び出す(という抽象的な問題)
* 特定の条件と具体的な処理を実装し、上の関数に登録する(という具体的な問題)
という二つに分解する。
```js:`todoReducer`内の実装で特定の条件と具体的な処理のペアが増えても、`createReducer`の実装は変わらない
const ActionType = {
ADD_TODO: 'ADD_TODO',
TOGGLE_TODO: 'TOGGLE_TODO'
}
// 任意の条件にマッチしたら対応する関数を呼び出す
function createReducer (initialState, handlers) {
return function reducer(state = initialState, action) {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action);
} else {
return state;
}
}
}
// 特定の条件と具体的な処理を実装し、上の関数に登録する
const todoReducer = createReducer([], {
[ActionType.ADD_TODO] (state, action) {
const text = action.text.trim();
return [...state, text];
}
});
こうすることでActionType
の種類が増えて実装を修正するときにも、createReducer
のテストケースを増やす必要はない。またtodoReducer
の実装もプロパティにActionCreatorを追加するだけでSwitch文などの制御自体をを増やす必要はない。さらに、createReducer
はどのプロジェクトにも再利用することができる。
(実装を追加しても、複雑さを増やさないという意味で)うん、エレガント。