React 应用SOLID原则03

React 应用 SOLID 原则 03

里氏替换原则(LSP)

里氏替换原则可以理解为对象之间的一种关系,子类型对象可以替换为超类型对象。这个原则严重依赖类继承来定义超类型和子类型关系,但它在 React 中可能不太适用,因为我们几乎不会处理类,更不用说类继承了。虽然远离类继承会不可避免地将这一原则转变为完全不同的东西,但使用继承编写 React 代码会使代码变得糟糕(React 团队不推荐使用继承)。因此,对于这一原则不在过多解释。

接口隔离原则(ISP)

定义:客户端不应该依赖它不需要的接口

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type Video = {
title: string
duration: number
coverUrl: string
}

type Props = {
items: Array<Video>
}

const VideoList = ({ items }) => {
return (
<ul>
{items.map(item =>
<Thumbnail
key={item.title}
video={item}
/>
)}
</ul>
)
}

Thumbnail 组件的实现

1
2
3
4
5
6
7
type Props = {
video: Video,
};

const Thumbnail = ({ video }: Props) => {
return <img src={video.coverUrl} />;
};

Thumbnail 这个组件非常简单,但它有一个问题,他希望将完整的视频对象作为 props 传入,但是仅有效的使用其属性之一(coverUrl)

除了视频,我们还需要渲染直播的缩略图,这两种媒体资源会混合在同一个列表中

定义直播类型:

1
2
3
4
type LiveStream = {
name: string;
previewUrl: string;
};

更新 VideoList 组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type Props = {
items: Array<Video | LiveStream>,
};

const VideoList = ({ items }) => {
return (
<ul>
{items.map((item) => {
if ("coverUrl" in item) {
return <Thumbnail video={item} />;
} else {
// 直播组件,该怎么写?
}
})}
</ul>
);
};

这时候就发现了一个问题,我们可以轻松区分视频和直播对象,但是不能将后者传递给 Thumbnail 组件,因为 video 和 liveStream 类型不兼容。他们包含了不同的属性来保存缩略图。视频对象调用 coverUrl,直播对象调用 previewUrl。这就使组件依赖了比实际更多的 props 的原因所在。

下面来重构 Thumbnail

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type Props = {
items: Array<Video | LiveStream>,
};

const VideoList = ({ items }) => {
return (
<ul>
{items.map((item) => {
if ("coverUrl" in item) {
return <Thumbnail coverUrl={item.coverUrl} />;
} else {
return <Thumbnail coverUrl={item.previewUrl} />;
}
})}
</ul>
);
};

接口隔离主张最小化系统组件直接的依赖关系,使他们的耦合度降低,从而提高可复用性。