「Keep It Simple, Stupid」(簡稱 KISS)是軟體開發中一項經典原則,強調程式碼應該保持簡潔、易懂。在 TypeScript 專案中,我們經常會遇到過度設計的情況:為了展示技術能力而使用複雜的設計模式、或是一個函式處理過多職責。
值得一提的是,KISS 原則與我們前面討論的「Write Everything Twice」和「Rule of Three」緊密相關。當我們避免過早抽象化時,自然會寫出更簡單、直接的程式碼。
先前在練習 Redux 的時候我撰寫了一個計數器,當時與一同在
寫 React 的工程師討論的時候就被告知不要這樣幹,當然那次是為了練習所以將簡單的計數器拆分,這裡以原生的 useReducer 和 useContext 為示範
import React, { createContext, useContext, useReducer, useState } from 'react';
// Action 類型定義
interface CounterAction {
type: 'INCREMENT' | 'DECREMENT' | 'RESET';
}
// 狀態介面定義
interface CounterState {
value: number;
}
// 建立 Action Creators
const createAction = {
increment: (): CounterAction => ({ type: 'INCREMENT' }),
decrement: (): CounterAction => ({ type: 'DECREMENT' }),
reset: (): CounterAction => ({ type: 'RESET' })
};
// 初始狀態
const initialState: CounterState = {
value: 0
};
// Reducer 函式
const counterReducer = (state: CounterState, action: CounterAction): CounterState => {
switch (action.type) {
case 'INCREMENT':
return {
...state,
value: state.value + 1
};
case 'DECREMENT':
return {
...state,
value: state.value - 1
};
case 'RESET':
return {
...state,
value: 0
};
default:
return state;
}
};
// Context 建立
interface CounterContextType {
state: CounterState;
actions: {
increment: () => void;
decrement: () => void;
reset: () => void;
};
}
const CounterContext = createContext<CounterContextType | undefined>(undefined);
// Context Provider 元件
const CounterProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [state, dispatch] = useReducer(counterReducer, initialState);
const actions = {
increment: () => dispatch(createAction.increment()),
decrement: () => dispatch(createAction.decrement()),
reset: () => dispatch(createAction.reset())
};
return (
<CounterContext.Provider value={{ state, actions }}>
{children}
</CounterContext.Provider>
);
};
// 自訂 Hook
const useCounter = () => {
const context = useContext(CounterContext);
if (!context) {
throw new Error('useCounter 必須在 CounterProvider 內使用');
}
return context;
};
// 複雜計數器元件
const ComplexCounter: React.FC = () => {
const { state, actions } = useCounter();
return (
<div>
<h2>複雜版本 (違反 KISS 原則)</h2>
<div>
<div>計數值:{state.value}</div>
</div>
<div>
<button onClick={actions.increment}>+1</button>
<button onClick={actions.decrement}>-1</button>
<button onClick={actions.reset}>重置</button>
</div>
</div>
);
};
這樣的寫法看似沒有什麼問題,只是單純只是簡單的計數器不需要添加這麼複雜的 redux 風格的程式碼,也不小心寫了許多Boilerplate Code這樣會造成開發者需要花費更多的時間去理解程式碼,因此我們只要保持簡單就好。
const SimpleCounter: React.FC = () => {
const [count, setCount] = useState(0);
return (
<div>
<h2>簡單版本 (符合 KISS 原則)</h2>
<div>計數值:{count}</div>
<div>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(count - 1)}>-1</button>
<button onClick={() => setCount(0)}>重置</button>
</div>
</div>
);
};
如上的程式碼保持簡單就好,當需要管理複雜的狀態並且跨應用程式的時候才需要使用 reducer 或者 context 來管理,這樣的寫法也符合 KISS 原則。
就像我們懂理解了 Redux 的撰寫模式但並不代表我們得一直使用 redux,Redux 官方網站在When Should I Use Redux也有提到對於複雜的狀態並且應用程式有許多大量的地方用到的時候才使用 Redux,Abraham Maslow 曾經提到工具定律,這個定律指出人們會在需要時才使用工具,而不是在工具可用時才需要。這個定律在軟體開發中同樣適用,引申到軟體開發中的詞彙則是叫做Gold Hammer Anti-Pattern,這是屬於一種反模式 (就是叫你不要這樣幹的模式),我們在撰寫程式碼的時候應當保持 KISS 原則使其更簡單易懂。