一個 類/模塊/元件 應該有且只有一個改變的原因。
(如果使用到多於一個的動機去改變一個 類/模塊/元件,那麼這個 類/模塊/元件 就具有多於一個的職責,不符合 SRP 原則)
Component
const getTitle = ({ type, title }) => {
switch (type) {
case 'a':
return 'A';
case 'b':
// ❌ 控制項變成兩個
if (title) return `B-${title}`;
else return 'B';
case 'c':
return 'C';
default:
return null;
}
};
const getContent = (type) => {
switch (type) {
case 'a':
return '是否刪除該筆資料?';
case 'b':
return '是否更新該筆資料?';
case 'c':
return '請確認內容無誤。';
default:
return null;
}
};
const getActions = ({ type, actions, onConfirm, onCancel }) => {
switch (type) {
case 'a':
case 'b':
return (
<div>
{actions.map(({ label, onClick }) => (
<Button onClick={onClick}>{label}</Button>
))}
</div>
);
case 'c':
return (
<div>
<Button onConfirm={onConfirm}>確認</Button>
<Button onCancel={onCancel}>取消</Button>
</div>
);
default:
return null;
}
};
// ❌ 把有不同的改變原因的事物耦合在一起的設計是糟糕的。
// 太多關注點需要留意,getTitle, getContent, getActions
const Dialog = (props) => (
<div>
<DialogTitle>{getTitle(props)}</DialogTitle>
<DialogContent>{getContent(props.type)}</DialogContent>
<DialogFooter>{getActions(props)}</DialogFooter>
</div>
);
// ✅ 應該把模組切開來,而非把所有判斷都寫在同一包裡面
const Dialog = (props) => {
const { type } = props;
// 可使用 switch case 區分模組
switch (type) {
case 'a':
return <ADialog {...props} />;
case 'b':
return <BDialog {...props} />;
case 'c':
return <CDialog {...props} />;
default:
return null;
}
};
// ✅ 也可透過物件方式區分
const dialogs = {
a: (props) => <ADialog {...props} />,
b: (props) => <BDialog {...props} />,
c: (props) => <CDialog {...props} />,
};
const Dialog = (props) => {
const { type } = props;
return dialogs[type](props) || null;
};
Function
// ❌ 一個 function 只能有一個控制項
const getRoutes = (type, name, subRoute) => {
switch (type) {
case 'a':
// ❌ 此時控制項就變成 type, name, subRoute
if (subRoute === 'project') {
return '/detail';
}
if (subRoute === 'project' && name === 'Gary') {
return `/${subRoute}/detail/${name}`;
}
return '/a/project/detail';
case 'b':
return '/b/home';
case 'c':
return '/c/settings';
default:
return '';
}
};
// ✅ 特殊規格獨立模組化,依照複雜度決定做成 function/class
const getTheARoutes = (name, subRoute) => {
if (subRoute === 'project') {
return name === 'Gary' ? `/${subRoute}/detail/${name}` : '/detail';
}
return '/a/project/detail';
};
const getRoutes = (type, ...rest) => {
switch (type) {
case 'a':
// ✅ 把該邏輯抽出來獨立成一個 function/module/class
return getTheARoutes(...rest);
case 'b':
return '/b/home';
case 'c':
return '/c/settings';
default:
return '';
}
};