为什么你的轮子很难用(二)从战略到抽象2

1,230 阅读14分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

画外音:

一直都在收拾了别人的烂摊子,总结一下怎么做一个"人见人夸"的功能设计。

这个系列主要写给产品看,最佳实践不敢说,失败案例我有大把可以分享和吐槽的。

研发请随意,有不妥的地方欢迎指正。

前情提要:

# 为什么你的轮子很难用(一):别造,造的话先盘组织和业务

# 为什么你的轮子很难用(二): 从战略到抽象1

当你不得不造一个轮子的时候,你需要先储备一些基础常识。内容很多,我们分期讲:

二、抽象的层级

第一节:功能是长出来的,但你要能倒着想

传统OOP面向对象涉及非常复杂的层次关系,且没有统一可执行的量化标准。

这里我给出一个自己常用的层级划分方法。

在此 先感谢 艾略特的波段理论 和 缠中说禅的缠论。

在股市技术分析理论的大小周期嵌套与推演关系锻炼了我的观察视角。

举个例子:

波浪中的小级别完成5浪3升=中级别的上升浪,

缠论的5分钟线顶底分型大于1根K线,可以构成 5分钟1完成1笔上升。

5笔之间价格重叠的部分可以构成一个中枢(比如:上-下-上-下-上)

5分钟1笔+1个中枢+1笔,可以构成 5分钟1段。

切换到更大的级别观察,5分钟一段 可以是 30分钟1笔。

所有大级别都是在不同层次中按照相同规律组合演进出来的。

那么回归到产研上:

首先,从大级别到小级别,从宏观到微观,

对于一个含有"大前端"业务的常用划分如下:

image.png

依照这个层级关系,我们可以粗略地进行定义:

满足>=1个实例的组合 可以是 1个模块

满足>=1个模块的组合 可以是 1个页面 

满足>=1个页面的组合 可以是 1个应用

满足>=1个应用的组合 可以是 1个系统

满足>=1个系统的组合 可以是 1个领域

满足>=1个领域的组合 可以是 1个分层

注意我的说法:可以是。别硬套,硬生生拆分。

还是需要考虑,这个 对象 本身是否足够 独立且完备的。

当然 如果考虑 父子逻辑,那么从7层变成13层,乃至更多:

image.png

父子逻辑乃至更深的树结构,一般只有

在较为复杂业务场景中,且多个模块必须进行强关联进行封装时,才会用到的。

举个例子

一个抽奖模块,在H5前端中 包含 剩余次数+奖品列表+抽奖按钮+背包弹窗+背景图+跑马灯。

那么回过头来,这个定义有个暂时没有解决的公理性的问题,什么叫一个实例。

就跟 技术分析炒股总是,不知道1浪从哪数起一样,然后就千人千浪。

从我个人角度,其实这就要拉平每一端的描述口径了(因为经常各自叫各自的,导致混乱)

尤其是在 app-path-module-entity 这最初的4级,

每一端叫法都会有些不同。

对于产品同学而言,日常打交道的,包括下图这5端的RD。(数据算法存储中间件运维咱们后续再讲)

image.png

其中 移动客户端(adroid和ios)pc客户端 web端 H5端 admin,又统称大前端。

那什么叫一个实例呢,

有些人说:对于前端而言叫 元素或控件,对后端来讲 叫 类或方法。二者之前通过1个乃至多个接口进行串联。

我觉得并不合理,此处我给出一个定义:能独立完成最小业务单元逻辑的对象。

接下来,我们从 最简单的 MVI 框架入手,仔细拆解一下。

假设,我们有一个需求,对A用户进行关注。让我们随便画一个关注功能:

image.png

那么此处该如何拆解呢?

前端:

- 元素:背景图,头像,昵称,关注按钮。

- 状态:未关注 和 已关注

- 交互:未关注状态 展示"关注", 点击 未关注 按钮,切换为 已关注状态,展示 "取消关注"

后端:

- 根据A用户的UID查询昵称和头像

- 查询当前登录用户是否关注A用户

- 发起当前登录用户对A用户的关注请求

- 发起当前登录用户对A用户的取关请求

......

而对于指定用户进行关注,这个对象已经是MVP最小闭环,

再往细了拆没有太大必要,对于业务而言麻烦或者跑不通了。

那么这就是一个实例。

这个时候运营给你提需求说,我想界面上展示 [a,b,c,d] 4个人,乃至更多。

那它有可能是横着排,也有可能竖着排。

你将 之前 做好的关注功能,多排几个 变成列表。

那就变成了:

image.png

此时,这个功能,就成了 由多个实例构成的模块。

   A 实例的内部逻辑:(同上)
   
   B 模块的内部逻辑:
   
      前端:
         
         - 元素:模块标题 + 多个关注实例组成的列表,跟行列个数排布
         
         - 交互:左右滑动 / 上下滑动 查看
          
      后端:
      
        - 根据用户的UID[a,b,c,d]分别查询昵称和头像

        - 查询当前登录用户是否关注[a,b,c,d]用户

        - 发起当前登录用户对指定用户的关注请求

        - 发起当前登录用户对指定用户的取关请求
      

那这时候 运营又说了:除了这个关注列表,再给我加个抽奖,加个任务,加个巴拉巴拉~

你应该非常熟练的开始撸prd:关注列表模块,抽奖模块,任务模块..嗯,他们都在一个页面里。

等等 关注用户=完成任务1=发抽奖次数,

为啥 我在:

 关注模块点击关注了,任务模块里任务的状态没变,

 在任务模块里领了抽奖次数,抽奖模块的抽奖次数没变?

是不是感觉还需要更高一层的东西,来 组织和控制?

恭喜你 又到了 页面path 这一层。

 页面层主要负责,单个模块无法独立完成的工作。对多个模块实现通信,和业务逻辑的组织。
 
 最典型的,比如 当A模块执行X逻辑时,需要通信B模块,使B的Y执行Z逻辑。

 像 当在任务列表模块中,领取了抽奖模块的抽奖次数,抽奖模块的次数需要刷新为最新值。
 

到这里,你已经大概明白 页面-模块-实例,三层的结构了。

接下来让我们统一再理解一遍:

第二节:培养初步的架构视角

这里我们以大前端为例:UI展示层-模块逻辑层-页面逻辑层

1.UI展示层

常用划分方案:

应用——页面——模块——元素(控件)

常规概念解释:

元素/控件: 最小颗粒度的展示

元素: 图片 / 文字

控件: 按钮 / 下拉框 / 输入框 

实例: 由 1到多个 元素/控件 构成,带有独立业务逻辑的单个对象

抽奖按钮:展示是否可抽奖,点击发起抽奖 

单人关注:头像+昵称+关注按钮

轮播大图:图片+轮播点+向左(右)

模块: 由 1到多个 同类 或 相关联的 实例构成的

同类:多个用户列表展示,引导关注 == 关注列表模块

关联:文本题目展示+多个按钮选项选择 == 多选题模块

父子模块: 多个需要相关联的模块组合构成,属于模块组合的一种高级形式

XX活动任务 == 任务总积分展示 + 每日任务列表 + 连续签到列表

一般常用于 页面内的纵向关联,极少用于横向(比如以父子模块实现带tab的筛选)

页面: 可以基于url进行独立访问,由多个 模块 使用一定的布局组合而成

页面内涉及 分区 or 分页,同页面内的上下左右各种切割进行排布

什么叫分区:比如左右布局,左半部分是搜索结果,右半边份是 广告+榜单等等

比如:年度盛典== tabA 赛道+报名 + tab B 赛程+赛道+主播排行榜+大R排行榜 

应用:

 单页面:不涉及跳转打开,主要采用页面内以tab切换渲染/滚屏分页/半窗弹出等

 多页面:采用跳转打开,多个页面独立维护开发

2.模块逻辑层:

对于任意对象:元素/控件 or 模块 都有相应的输入/输出逻辑,比如

数据逻辑: 从 M 根据 X 取 Y, 在 N 进行数据展示

   eg: 关注列表模块

  主播数据:从后台配置获取,根据 运营上传配置的UID 在 XXX 展示 头像 / 昵称 / 开播态

  关注数据:在页面加载时请求,根据 登录用户UID,拉取对应主播UID的关注状态,在XXX展示成XXX

交互逻辑: 一共有哪些状态,怎么展示,不同状态有什么交互

   eg:关注列表模块

    主播开播数据:

      A 开播中状态,展示开播态标识,点击 头像 跳转 直播间

      B 未开播状态,不展示开播态标识,点击头像 跳转 用户个人页

    主播关注数据:

      A   未关注状态:展示 关注 ,点击后 切换为已关注

      B   已关注状态:展示 已关注,按钮置灰,不可点击

     列表展示数据:

      默认展示5行,超出5行滚动展示 / 分页展示。

      仅页面加载时刷新到最新数据。

常用的交互用例:

  行为动作:加载 / 点击 / 曝光 / 跳转 / 弹出打开 / 组件内上下滚动 / 页面整体滚动

  控件交互:弹窗(确认/取消/关闭) / 半窗弹窗(确认/取消/关闭) / toast   埋点逻辑: 怎么埋点上报,需要带什么参数信息

eg: 模块ID--模块名称--交互行为(曝光/点击)-点击主播UID-点击用户UID-时间戳

胶水逻辑: 模块与模块之间的联动与通信

eg:  当A模块执行,对B模块 发送 C 信号,

在任务模块点击领取抽奖次数后,通知抽奖模块内的抽奖次数与抽奖按钮更新为XX状态

3. 页面逻辑层(页面胶水):

  除了为同一页面内涉及多个模块之间的通信基础提供能力,

还可以对多个子页面,多个模块之间的联动进行有效组织

eg:页面内 A模块:购买领取礼包 B模块:签到领取礼包 都复用同一个礼包弹窗 

eg:单个模块触发登录和绑定游戏角色后,需要同时触发多个模块的更新到已绑定状态

eg:未领取过战队勋章,进入领取页,已领取过展示 个人信息和历史成就。

4.  如何优雅的划分前端功能?

1.如何优雅的划分,确认某层tab应该在页面层级 还是 模块内层级?

 参考标准:

页面tab一般不超过3级,建议涉及第4级tab封装进模块内部

页面级常用3级模式:底导-顶导一级tab-顶导2级tab

其中:每个底部导航对应单一页面,单一页面保持2层即可,即 顶导一级tab-顶导2级tab

    

2.如何优雅的划分出模块,并写 清楚彼此之间的胶水关联关系 ?

对原型图画框框,大框套小框,粗框套细框,区分清楚 页面 / 分区 / 模块(父子层级)

来个表格试试填一下?

页面层逻辑:对所有页面进行总体说明 

 1.页面概述:一共有M个页面N个模块
 A 页面:XXXXXXX 
 模块1:标题xxxx
 模块2:标题xxxx
 B 页面:YYYYYYY
 分区1:yyyyy
 模块1 :标题mmmmm
 模块2: 标题nnnnn
 ...             
 2.页面胶水
 当未登录时,不显示A页面和B页面,仅显示C页面
 当已登录时,展示A页面和B页面,不显示C页面
 

模块详述:对单个模块进行书写

  所属页面:页面XXXXX
  所属分区:分区XXXXX
  父模块1:标题XXXXXX
  模块综述:该模块由 A+B+C 三个子模块组成 
  胶水逻辑:三个子模块,默认展示第一个。展开其他时,其余收起。
   
   子模块A:标题XXXXX
    前端逻辑  
    A 实例1:XXXXX
     1)展示逻辑: 从M,根据 X 取 Y ,在N 进行数据展示
     2)交互逻辑: 一共有哪些状态,怎么展示,怎么操作,不同状态有什么交互 
     3)胶水逻辑:(与其他前端对象的联动逻辑)当XXX模块发生XXX行为,此时该模块的XXX需要变更XXX
     4)埋点逻辑: 模块ID--模块名称--交互行为(曝光/点击)上报内容{ xxxx , xxxx }
    B 实例2:XXXXXX
     1)展示逻辑: 从M,根据 X 取 Y ,在N 进行数据展示
     2)交互逻辑: 一共有哪些状态,怎么展示,怎么操作,不同状态有什么交互 
     3)胶水逻辑:(与其他前端对象的联动逻辑)当XXX模块发生XXX行为,此时该模块的XXX需要变更XXX
     4)埋点逻辑: 模块ID--模块名称--交互行为(曝光/点击)上报内容{ xxxx , xxxx }
    后端逻辑: 
    A 接口1:XXXXXXX
     1)依赖逻辑:从哪个后台配置什么后端功能, ID是多少
     2)实现逻辑:使用什么数据,计算什么,判断什么,
     3)存储逻辑:数据存多久,如何更新
     4) 接口逻辑:出参是...,入参是......
    B 接口21)依赖逻辑:从哪个后台配置什么后端功能, ID是多少
     2)实现逻辑:使用什么数据,计算什么,判断什么,
     3)存储逻辑:数据存多久,如何更新
     4) 接口逻辑:出参是...,入参是......
     
   子模块B:标题XXXXX
    前端逻辑  
    A 实例1:XXXXX
     1)展示逻辑: 从M,根据 X 取 Y ,在N 进行数据展示
     2)交互逻辑: 一共有哪些状态,怎么展示,怎么操作,不同状态有什么交互 
     3)胶水逻辑:(与其他前端对象的联动逻辑)当XXX模块发生XXX行为,此时该模块的XXX需要变更XXX
     4)埋点逻辑: 模块ID--模块名称--交互行为(曝光/点击)上报内容{ xxxx , xxxx }
    B 实例2:XXXXXX
     1)展示逻辑: 从M,根据 X 取 Y ,在N 进行数据展示
     2)交互逻辑: 一共有哪些状态,怎么展示,怎么操作,不同状态有什么交互 
     3)胶水逻辑:(与其他前端对象的联动逻辑)当XXX模块发生XXX行为,此时该模块的XXX需要变更XXX
     4)埋点逻辑: 模块ID--模块名称--交互行为(曝光/点击)上报内容{ xxxx , xxxx }
    后端逻辑: 
    A 接口1:XXXXXXX
     1)依赖逻辑:从哪个后台配置什么后端功能, ID是多少
     2)实现逻辑:使用什么数据,计算什么,判断什么,
     3)存储逻辑:数据存多久,如何更新
     4) 接口逻辑:出参是...,入参是......
    B 接口21)依赖逻辑:从哪个后台配置什么后端功能, ID是多少
     2)实现逻辑:使用什么数据,计算什么,判断什么,
     3)存储逻辑:数据存多久,如何更新
     4) 接口逻辑:出参是...,入参是......
     

当你熟练掌握 前端层面 的 应用——页面——模块——元素(控件)的划分方法,

并且可以大概地想一想对应的后端逻辑时,那我们就可以来到 复用性的环节,

也就是把好多人搞晕的:组件

预知后事如何,且听下回分解。