最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 谈谈日常写代码中关于设计模式的一些思考

    正文概述 掘金(彭道宽)   2021-03-19   472

    前言

    ✋ 本文不讲干巴巴的理论,直接就上日常业务开发过程中的一些设计思考,当然也不一定正确,如有错误,欢迎指出~

    前段时间,不对,应该是差不多一年了,在 2020.04 月份发过一篇关于设计模式的文章:前端渣渣唠嗑一下前端中的设计模式(真实场景例子),时隔一年,今天从工作笔记中捞出日常做的笔记,整理成一篇小文章,当作是小彩蛋吧~

    正文

    下面举两个小例子,聊聊我的一些思考,不一定正确,如果写错了,或者有更好的方案,欢迎评论区指出~

    业务一

    之前场景

    调用 api_1 接口,返回一组数据 MaterialsList ,将 MaterialsList 传给 <ViewList /> 组件,从而进行展示,如下图所示

    <ViewList MaterialsList={MaterialsList} />
    
    谈谈日常写代码中关于设计模式的一些思考

    现在场景

    正常情况下,不会出现什么问题,但,人算不如产品算,产品过了大半年,突然有一天想动这一块了,如下图所示:

    谈谈日常写代码中关于设计模式的一些思考

    看这个图,图中红色方框区域是我们已经封装好的 <ViewList /> 资源列表展示组件,该组件接收的 MaterialsList 从上边的一处来源(原来只通过 api_1)变成了三处来源:

    • 本地上传文件的数据格式
    • 接口 api_1 返回的数据格式
    • 接口 api_2 返回的数据格式

    怎么理解呢?可以看到图中,该流程主要是:

    • 右边的“资源概况”是调接口 api_1,返回的一个 MaterialsList ,可以从右边点击 “+” 添加进来
    • 也可以通过选择本地文件上传
    • 如果是编辑场景下,数据为 api_2 接口返回的数据格式

    那么在当前场景下,这个资源预览组件已经不能符合我们的期望;在历史旧接口无法改动的情况下,结合现有新增接口以及考虑之后可能会存在更多种数据结构来源,所以采用适配器模式,对多种数据结构进行适配,从而满足需求

    适配器模式

    先来看看原先设计和适配器模式之后的对比图

    谈谈日常写代码中关于设计模式的一些思考

    原先设计,该组件专门为此数据源结构服务,当该组件的数据源多一种结构时,就不适用。

    从设计上看,变的是数据来源,不变的是展示组件的UI和展示数据的数据结构,所以我们只需要定义该组件展示的统一数据结构,通过适配器适配成组件展示的数据格式,尽管之后再来多少种数据源,我们只需要添加一种适配规则即可。

    代码展示

    首先,定义一个统一的数据格式:AdapterResourceType

    export interface AdapterResourceType {
      uuid: string;
      created: number;
      fileNo: number;
      fileName: string;
      fileOrigin: string;
      fileStatus: string;
      fileInfo: {
        type: number;
        size?: number;
        [key: string]: any;
      };
      // 本地图片额外操作
      action?: {
        cancel?: () => void;
        [key: string]: any;
      };
    }
    

    然后通过适配器模块,适配成我们需要的接口 API,以满足我们的需求

    const AdapterResource = {
      // 本地文件
      local(list: ResourceLocalType[]) {
        if (list && list.length === 0) return [];
        const adapterList: AdapterResourceType[] = list.map((resource: ResourceLocalType) => {
          return adapterToLocal(resource);
        });
        return adapterList;
      },
      // api_1
      api1(list: ResourceAPI1Type[]) {
        if (list && list.length === 0) return [];
        const adapterList: AdapterResourceType[] = list.map((resource: ResourceAPI1Type) => {
          return adapterToAPI1(resource);
        });
        return adapterList;
      },
      // api_2
      api2(list: ResourceAPI2Type[]) {
        if (list && list.length === 0) return [];
        const adapterList: AdapterResourceType[] = list.map((resource: ResourceAPI2Type) => {
          return adapterToAPI2(resource);
        });
        return adapterList;
      },
    };
    

    在数据进行组件列表展示时,将来源不同的数据经过适配器处理,进行整合,然后传递给 <ViewList /> 展示组件,以达到我们的目的

    const local_resource = useSelector((state: any) => state.resource.local);
    const api1_resource = useSelector((state: any) => state.resource.api1);
    const api2_resource = useSelector((state: any) => state.resource.api2);
    
    useEffect(() => {
      const localAdapterResource = AdapterResource.local([...Array.from(local_resource)]);
      const api1AdapterResource = AdapterResource.api1([...Array.from(api1_resource)]);
      const api2AdapterResource = AdapterResource.api2([...Array.from(api2_resource)]);
      
      setViewList([...localAdapterResource, ...api1AdapterResource, ...api2AdapterResource])
    }, [local_resource, api1_resource, api2_resource])
    

    之后如果再来一种新的数据源,只需要在 AdapterResource 适配器模块中添加对应的适配规则即可。

    业务二

    先看图片说明,然后谈谈我的一些想法...

    谈谈日常写代码中关于设计模式的一些思考

    主要谈的是资源上传模块,上图所示,存在 添加图片添加音频,那么后面会不会出现更多的如 添加视频添加文档... 等需求的出现?

    之前设计

    我们来看看之前的设计,一个 Resource 下包含多个资源上传子组件,内部实现一些特定的兼容逻辑。当再添加一种新的资源上传组件,可能需要修改 Resource 内部逻辑以达到新组件的兼容适配,设计不合理

    谈谈日常写代码中关于设计模式的一些思考

    下边的是伪代码,大概是这样的

    function Resource() {
      // 发送数据前,需要做数据处理
      // 因为后台接口要求的数据格式是统一的,所以对于 phone、audio 做对应的一个数据兼容
      const onSend() {
          const phonParams = phoneList.map((p) => {
            // 图片的处理
          })
          const audioParams = audioList.map((p) => {
            // 音频的处理
          })
          const params = [...phonParams, ...audioParams]
          // 巴拉巴拉,结构处理完之后,进行发送
          onSave(params)
      }
      return (
        <Wrapper>
          <UploadPhone />
          <UploadAudio />
        </Wrapper>
      ) 
    }
    

    从组件层级来看,Resource 是向下依赖子组件(依赖关系向下),此时需要新增C组件,可能还需要针对 C 组件进行一些特定的兼容。将来如果子组件类型增多,意味着需要频繁修改 Resource 内部逻辑!!!

    从接口层面来看,Resource 内部组合所有的资源组件数据,进行相应的数据处理,然后对外导出。

    之后设计

    从设计上看,变的是子组件类型,不变的是对外导出的数据结构以及 Resource 组件。我们期望,不因后续持续添加的子组件,频繁修改 Resource 内部逻辑,那么我们这么设计,将 Resource 变成稳定“系统”,定义一套 IResource 数据接口,所有的资源上传子组件都必须实现此接口。

    谈谈日常写代码中关于设计模式的一些思考

    从组件层级来看,Resource 不再依赖子组件,通过插件化形式添加子组件(依赖关系向上),所有的子组件都需要实现统一的 IResource 接口,即使后面再添加新的资源上传子组件,都不需要修改 Resource 内部逻辑

    从接口层面上看,Resource 不需要做数据的处理,特定兼容由外部自行完成(外部需要实现 IResource 接口)

    之后你再添加多少种新的子组件,你都需要自己实现我的 IResource 接口,然后把组件加进来就好了,这样在相对较长的一段周期内,我的 Resource 是处于稳定状态,而不是如之前设计那般,来一种子组件,我就需要该 Resource 内部逻辑,长期处于“动荡”的情况~

    最后

    暂时讲两个,有时间再继续写...

    相关链接

    • 阿宽的博客
    • 团队博客
    • 前端渣渣唠嗑一下前端中的设计模式(真实场景例子)
    • 知乎-【干货】React 组件插件化的简洁实现

    对了,未经原博主同意,禁止转载,不然我见一个,举报一个,有点硬~~


    起源地下载网 » 谈谈日常写代码中关于设计模式的一些思考

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元