首页   

React 中的接口隔离原则

奇舞精选  · 设计 前端  · 1 月前

本文为翻译
本文译者为 360 奇舞团前端开发工程师
原文标题:Interface Segregation Principle in React
原文作者:Alex Kondovt
原文地址:https://alexkondov.com/interface-segregation-principle-in-react/

React 中的接口隔离原则

SOLID 原则是我学习的第一个软件设计概念,时至今日,它们仍然是对我的职业生涯影响最大的知识。如果没有它们,也许我永远不会开始关注代码的质量和项目的结构。

尽管它们最适合面向对象的开发,但无论我在什么环境和模式下工作,我都会将它们牢记在心。

说有一条 SOLID 原则我能够应用到任何地方,那就是关于接口隔离的原则。

接口隔离原则

该原则是指我们应避免创建包含许多方法或值的大型接口。相反,我们应该创建更小的接口,以满足使用这些接口的函数或类的需要。

如果你想了解一下历史,据我所知,该原则是施乐公司提出的,当时他们的软件只有一个 “工作 ”类,负责你可以执行的所有任务。随着时间的推移,这个类的可维护性成了问题。

interface Job {
    fax(): void;
    scan(): void;
    print(): void;
}

function print(job: Job{
    // We're using only a single method from the interface
    // But we expect the entire interface to be implemented
    job.print();
}

于是,他们将其拆分成更小的类和接口,这些类和接口只负责特定的任务,这样代码就变得更简单了。

interface PrintJob {
    print(): void;
}

function print(job: PrintJob{
    // We only expect the interface to implement the print method
    job.print();
}

这只是一个简单的例子,旨在说明问题:使用较小的接口不仅更易于实现和维护,而且也更易于测试。

然而,当我学习这些原则时,我发现很难将它们转化为前端开发。我交互的唯一接口是 prop 定义。而且它们总是特定于组件,重用大型接口并不是真正的问题。

但几年后,我发现 “不依赖于不需要的值 ”这一抽象原则实际上在 React 中也有用武之地。

依赖庞大的组件

想象一下,我们有如下组件,它希望将用户对象作为prop传递给它:

interface Props {
    user: User;
}

function UserGreeting({ user }: Props{
    return 

Hey, {user.name}!</h1>;
}

不要关注引用相等或重新渲染的问题,它们不是现在的重点。

我们的组件需要一个用户对象,这是一个必须的prop,所以我们提供了它。但仔细观察组件的实现后,我们发现它实际上只使用了一个值--name。

这违反了接口隔离原则。

最后一句话听起来很隐晦,但不用太在意。这个组件只是有点欺骗性。它向使用它的开发人员表明,它需要一个对象,尽管它只使用了对象的一小部分。但在编程中,就像在生活中一样,诚实是有帮助的。

如果我们依赖于整个对象,可能会增加组件的使用难度。如果我们能明确自己需要的值,那就更好了。

interface Props {
    name: string;
}

function UserGreeting({ name }: Props{
    return <h1>Hey, {name}!h1>;
}

我们可以用代理指标来衡量我们是否改进了设计,那就是评估使用此 API 进行测试是否更容易。

使用以前的道具,我们在测试组件时必须模拟整个用户对象。对用户对象的任何改动,如添加额外的字段,都必须反映在组件的测试中,即使这些改动是不需要的。

但有了这种实现方式,我们只需更改组件所期望的原始值,而不会受到对象未来变化的影响。

这无疑更简单。

Prop Drilling

违反这一原则的另一种常见方式是Prop Drilling。这是所有前端框架中常见的一种反模式。当我们将一个值传递到多个不需要该值的组件时,就会发生这种情况。

function Dashboard({ user }{
    return (
        <section>
            <Header />
            ...
        section>

    )
}

function Header({ user }{
    return (
        <header>
            <Navigation user={user} />
        header>

    )
}

function Navigation({ user }{
    return (
        <nav>
            <UserGreeting name={user.name} />
            ...
        nav>

    )
}

function UserGreeting({ name }{
    return <h1>Hey, {name}!h1>;
}

这是一个问题,因为我们又一次欺骗了代码的读者。通过观察我们组件的道具,他们会认为他们需要用户对象以便从中渲染一些内容,但事实上,他们只是将用户对象传递给他们的子组件。他们实际上并没有使用它。

在这种情况下,我们需要寻找一种不同的解决方案。

在 React 中,最常见的做法是使用上下文或使用状态管理库,让我们的组件直接读取值。

function UserGreeting({
    const user = useUser();
    return 

Hey, {user.name}!</h1>;
}

但我们经常忘记的另一个选择是组件合成。

function Dashboard({ user }{
    return (
        <section>
            <Header>
                <Navigation>
                    <UserGreeting name={user.name} />
                Navigation>
            Header>
        section>

    )
}

这样我们就不会将值传递给不需要它们的组件。

接口隔离化繁为简

这个想法的美妙之处在于它可以被描述得如此简单--代码不应该依赖于它不使用的值和方法。如果你不需要某些东西,就不要要求它。就是这么简单。

上述示例中唯一的接口就是道具定义。我们没有做任何比平时更复杂的事情。重要的是,我们遵循的是主要原则,而不是任意实现。

我们不希望我们的代码依赖于它不需要的东西,这就是接口隔离的意义所在。



- END -

如果您关注前端+AI 相关领域可以扫码进群交流


 


添加小编微信进群😊


关于奇舞团

奇舞团是 360 集团最大的大前端团队,非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。


© 2024 精读
删除内容请联系邮箱 2879853325@qq.com