提升前端代码质量之SOLID设计原则-ISP接口隔离原则

635 阅读3分钟

前言

在提升前端代码质量之SOLID设计原则系列中文章提升前端代码质量之SOLID设计原则-SRP单一职责已经简单介绍了SOLID五种设计模式,不清楚可以移步到上文,本文不再赘述。

SOLID相关的文章索引:

本文结合代码(React)主要讲解 SOLID 原则中的ISP - 接口隔离原则

接口隔离原则(Interface Segregation Principle)

客户端不应该依赖它不需要的接口;类间的依赖关系应该建立在最小的接口上。

简单的说就是使用者不应该被迫依赖于它不使用的方法。比如我只是普通的发烧,我只需要去药店购买布诺芬就行了,但是药店只有捆绑销售:布诺芬+对乙酰氨基酚滴剂+对乙酰氨基酚栓+氨基比林等(降烧药类)。我只需要布诺芬,你给这么多显然是不对的,我吃了万一出事了咋办。

回到具体的设计层面来说,如果接口功能过多,非常容易违反单一职责

而对于我们的编码来说,就是从调用方的需求出发,需要调用什么,我就提供什么。

尽可能提供少接口或者分割多个接口,而不是堆积接口方法。 就拿之前我们的例子提升前端代码质量之SOLID设计原则-SRP单一职责里面的来说,例子中商品展示的图片我们把它抽离出来封装成一个image组件:

//src\page\ISP\Product.tsx
import React from 'react';
import { TaobaoCircleOutlined } from '@ant-design/icons';

interface IProduct {
  img?: string;
  title?: string;
  price?: number;
  discount?: number;
  storeName?: string;
  id?: string;
}

interface IShowProductProps {
  data: IProduct,
}
export default (props: IShowProductProps) => {
  const { data: productItem } = props;
  return <>
    <div
      style={{
        padding: '16px',
        margin: '8px',
        display: 'flex',
        flexDirection: 'column',
        border: '1px solid #f2f2f2',
        width: '234px',
        height: '316px'
      }}>
      <img src={productItem.img} />
      <h3
        style={{
          overflow: 'hidden',
          whiteSpace: 'nowrap',
          textOverflow: 'ellipsis'
        }}
      >{productItem.title}</h3>
      <div>
        <span
          style={{
            color: 'red'
          }}
        >
          ¥{(productItem.price * (productItem.discount || 1)).toFixed(2)}
        </span>&emsp;
        {productItem.discount && <span style={{ textDecoration: 'line-through', opacity: '0.6' }}>¥{productItem.price.toFixed(2)}</span>}
      </div>
      <div>
        <TaobaoCircleOutlined style={{ fontSize: '12px', color: '#fd3f31' }} />
        <span style={{ opacity: '0.6', fontSize: '12px' }}> {productItem.storeName}</span>
      </div>
    </div>
  </>
}

封装Image组件过后:

//src\page\ISP\Image.tsx
import React from "react"
const Image = (props: { img?: string }) => {
  const { img } = props
  return <>
    <img src={img} />
  </>
}
export default Image;

//src\page\ISP\Product.tsx
import React from 'react';
import { TaobaoCircleOutlined } from '@ant-design/icons';
import Image from './Image'
...
export default (props: IShowProductProps) => {
  const { data: productItem } = props;
  return <>
    <div
      style={{
        padding: '16px',
        margin: '8px',
        display: 'flex',
        flexDirection: 'column',
        border: '1px solid #f2f2f2',
        width: '234px',
        height: '316px'
      }}>
        <Image {...productItem}/> 
      ...
    </div>
  </>
}

能看到Image组件的props接收productItem:IProduct里面的属性:

interface IProduct {
  img?: string;
  title?: string;
  price?: number;
  discount?: number;
  storeName?: string;
  id?: string;
}

其实Image只需要一个属性:图片的url(这里是img),但是却接收除了img属性以外,还接收了额外属性(title、price、discount、storeName、id)。
正确的传参方式:

//src\page\ISP\Product.tsx
import React from 'react';
import { TaobaoCircleOutlined } from '@ant-design/icons';
import Image from './Image'
...
export default (props: IShowProductProps) => {
  const { data: productItem } = props;
  return <>
    <div
      style={{
        padding: '16px',
        margin: '8px',
        display: 'flex',
        flexDirection: 'column',
        border: '1px solid #f2f2f2',
        width: '234px',
        height: '316px'
      }}>
        <Image img={productItem.img}/>
      ...
    </div>
  </>
}

接口隔离原则最重要的就是,从使用者角度出发定义接口方法。像antd这些组件库,都提供了按需加载能力,这个其实从某种意义上来说就是接口隔离,因为对于不需要的功能我们不必要都跑一遍。

最后

接口隔离原则ISP理解相对简单,也就没啥总结的了

相关的代码已经整理成一个仓库上传至github,感兴趣可以结合仓库代码阅读REACT-SOLID