接續 SOLID 原則在 React 的實踐(一),今天會繼續寫~
“Objects of a superclass should be replaceable with objects of its subclasses.“
雖然在原本 LSP 概念中指的是 superclass 的 subclass 可以替換其 superclass 而不會破壞掉功能。像 RacingCar
是 Car
的 subclass,那麼我們應該能夠用 RacingCar
替換 Car
的實例。換句話說,RacingCar
必須滿足 Car
類別所設定的所有預期行為。
在 React 中 LSP 的應用是,當我們替換一個父元件時,子元件應該能夠繼續執行相同的工作。也就是說,如果一個元件使用了另一個元件,那麼即使替換了父元件,應用程式還是能正常運作,不會因為替換了元件而破壞其行為。直接看範例吧:
範例:
假設我們有一個基礎的 <ImageDisplay>
元件,並且我們想要擴展它來支持不同的顯示風格:
// 基本的顯示圖片功能
const ImageDisplay = ({ src, alt }) => {
return (
<img src={src} alt={alt} className="w-full h-auto" />
);
};
接下來, <RoundedImageDisplay>
在基本功能上添加了圓角樣式:
// 顯示圖片加上圓角的樣式,擴展了 <ImageDisplay>
const RoundedImageDisplay = ({ src, alt }) => {
return (
<ImageDisplay
src={src}
alt={alt}
className="w-24 h-24 rounded-full object-cover"
/>
);
};
<RoundedImageDisplay>
擴展了 <ImageDisplay>
,並且能夠使用 <ImageDisplay>
元件的所有功能,但添加了額外的樣式。確保符合 LSP - 做替換測試
以下範例可以看到能安全地用 <RoundedImageDisplay>
替換 <ImageDisplay>
,而不會改變行為或破壞功能,因此符合 Liskov Substitution Principle (LSP)。
// 在 App 中使用 <ImageDisplay> 顯示圖片:
const App = () => {
return (
<div>
<ImageDisplay src="image.jpg" alt="Description" />
</div>
);
};
// 在 App 中能安全地用 <RoundedImageDisplay> 替換 <ImageDisplay>
const App = () => {
return (
<div>
<RoundedImageDisplay src="image.jpg" alt="Description" />
</div>
);
};
“No code should be forced to depend on methods it does not use.“
在物件導向中,ISP 指的是不應該強迫一個類別(class)實作它不使用的介面(interface)和方法(method)。
那在 React 中,可以是「元件不應依賴它們不使用的 props」,或是「React hooks 可以幫助你將不同的行為邏輯拆分」
範例 - 元件不應依賴它們不使用的 props:
假設一個 <ListItem>
元件只需要顯示用戶的名字和電子郵件:
// ❌ 錯誤範例:違反 ISP,<ListItem> 只需要 image, title, description
const ListItem = ({ item }) => {
return (
<View>
<Image source={{ uri: item.image }} />
<Text>{item.title}</Text>
<Text>{item.description}</Text>
</View>
);
};
<ListItem>
元件接收一個 item
prop,但這個 item
prop 可能包含許多其他資料。<ListItem>
元件只需要 item
中的 image
、title
和 description
三個字段,這樣的設計可能會導致不必要的依賴為了遵守 Interface Segregation Principle(ISP),我們可以將 <ListItem>
元件的 props 精簡為僅包含它實際需要的字段:
// ⭕️ 正確做法:傳入 <ListItem> 需要的 image, title, description
const ListItem = ({ image, title, description }) => {
return (
<View>
<Image source={{ uri: image }} />
<Text>{title}</Text>
<Text>{description}</Text>
</View>
);
}
item
prop 中獲取不必要的資料,從而減少了不必要的依賴和潛在的錯誤。