历史遗留及代码问题
- 千行以上的代码
缺少结构化的思考、缺少模块划分能力。
- 耦合严重
高耦合意味着各个部分之间的依赖性很强,一个部分的改动可能会影响到其他部分,或者每当需要修改一个部分时,可能需要同时修改多个其他部分,增加了维护的难度,这样的代码结构容易引入bug,并且修改一个地方可能会导致意想不到的副作用。
- 代码复制
没有复用,抽组件、方法,为长期维护埋坑(全局搜索一大堆,一处改,处处改)
- 破坏了数据一致性
代码前 需设计数据结构:区分哪些是内部状态state、哪些是派生状态computed
- 职责单一
重构
重构是指在不改变软件功能的前提下,对代码进行修改和调整其结构,目的是提高代码的可读性和可维护性,降低修改成本。这种修改过程不会改变软件的外部行为,即程序的行为和结果对于外部使用程序(如程序员)来说没有任何变化。
如何重构
- 不改变软件的功能:不影响模块对外的功能,即不改变接口
- 小步快跑:不要一下子改太多,否则出了问题不容易查找原因,也不容易回退,
- 边改边测:一定要改一小块测一小块,步步为营
- 随时可停:重构要能随时终止,不影响业务的开发,这样也方便只在自己空闲时间重构,不占用项目排期时间
代码前设计
对齐需求认知:
了解本次需求变更的原因和背景,知道做这个功能的原因和意义 了解本次变更的需求范围,哪些是新增功能,哪些是变更功能,本次需求会涉及哪些模块的修改 及时发现需求中的遗漏、缺陷,及时通知产品调整需求 明确每个功能模块的具体交互细节、交互流程,比如每个按钮什么情况下可以点击,什么情况禁用,点击之后处理流程是什么
降低项目风险:
根据需求的工作量合理预估工时,给出一个靠谱的项目排期 提前暴露风险,不突然给领导"惊吓" 有了设计文档,才能让其他经验丰富的同事帮自己评审把关,提前发现设计上的缺陷
提升编程能力:
设计中会写伪代码,而写伪代码的过程就类似大脑编译代码的过程,有助于提升逻辑思维能力,最终将自己的大脑训练成"人肉逻辑编译器" 在设计各个模块的对外接口和内部实现中,思考如何提升复用性、扩展性以及如何解耦
提升大型项目设计能力:
设计的过程就是大脑模拟解决问题的过程,通过日常训练,提升结构化、系统化、抽象化思考的能力,做到比别人想的多,想的深 先从小项目的设计开始,通过反复训练,提升大型项目的设计能力
业务功能设计文档
相关文档
在开发过程中,经常需要查看各种文档,比如产品需求文档、API设计文档、API接口说明、UI图等,可以把这些文档链接集中放到设计文档中,减少开发中查找文档的时间,我习惯把这些相关文档放到设计文档的最顶部,而不是最下面,方便开发时使用。
需求梳理
需求的背景:
当前项目存在什么问题,为什么需要开发这个功能,了解本次需求的意义和价值。 了解了需求背景,你才能评价后面的产品设计是否解决了当前问题,有没有更好的解决方式,你才能给产品提出你的意见。
核心业务流程:
这部分不是必须的,根据情况而定,主要展示本次需求中几个核心功能的处理流程、状态流转等,让人对整体需求有个概念。
业务需求梳理
按照"业务模块--页面"或"业务模块--页面--功能点"的层次来组织我们的设计文档,如"用户管理--用户列表"、"用户管理--用户列表--新增用户弹窗"。
同时在文档中标注清楚是新增功能还是修改原有功能,如果是修改原有功能,则只需列出本次修改的功能点,把所有涉及的变更都列出来,开发时对着修改项进行开发即可,不再需要思考。
前端设计
在文档中,我们还应该针对一些重点/复杂的功能进行详细的前端编码设计,包括页面路由设计、全局状态设计、重点模块的数据结构、重点页面的组件Dom构成、组件的属性props/内部状态state/重点方法methods以及对外抛出的事件events等。
路由设计示例:
- 用户详情页路由:
- 形如:/user/:id?from=userList
- 路由参数:
- id: 用户id
- query:
- from: 代表页面跳转的来源,返回时根据from进入不同的页面,可选值 userList | orderDetail,默认userList
重点数据结构展示示例:
// 全局状态userInfo数据结构
{
"name": "",
"isVip": "",
"roles": []
}页面的伪代码:
<!-- 省略 -->要重视数据结构的设计
进行前端设计有诸多好处:
设计的过程就是对齐需求认知的过程,加深对产品需求的理解; 进行前端设计之后的排期才是靠谱的,而且能提前发现潜在的风险; 写伪代码的过程就类似大脑编译代码的过程,有效提升编程能力 设计的过程就是大脑模拟解决问题的过程,有效提升大型项目的组织、设计能力 前端设计文档分为业务功能设计文档和前端公共功能设计文档。
业务功能设计文档主要包括:相关文档、需求梳理、前端设计、依赖资源(API)、排期。
公共功能设计文档主要包括:当前存在的问题、期望实现的功能、使用示例、API、实现方案。
写好设计文档需要注意以下几点:
明确文档的目的和受众 结构化地表达 内容详尽、重点突出 图形化表达 在设计文档中经常会用到流程图、泳道图、架构图、状态机图和原型图,这些图形化表达方式可以很直观地展示出设计思路和细节,让评审人员快速抓住重点。
良好的设计不仅不会耽误项目进度,而且还能加快开发进程,缩短开发和维护周期,避免返工风险,如果你是项目管理者,更应该推动设计工作,将其作为研发流程中必要的一环。
你需要知道的一些知识
最少知识原则(The Least Knowledge Principle)
最少知识原则是一种面向对象的设计原则,它的核心理念是只与你的直接朋友交谈,不跟'陌生人'说话,强调对象之间应该尽量减少彼此的依赖关系,即一个类对于其他类知道的越少越好,一个对象应当对其他对象有尽可能少的了解
DRY原则(Don't Repeat Yourself)
DRY原则(Don't Repeat Yourself)是一种重要的编程原则,强调避免代码重复。它的核心思想是,任何一段代码都应该在系统中只有唯一的一份存在,而不应该重复出现。
KISS原则
KISS原则是“保持简单和愚蠢(Keep It Simple and Stupid)”的缩写,强调在解决问题或设计产品时要保持简单性和易用性。
YAGNI原则
YAGNI原则是软件开发中的一种原则,它代表着"You Ain't Gonna Need It",意思是"你不会需要它"。这个原则的核心思想是在开发过程中,不要去实现那些当前并不需要的功能。
YAGNI原则的目的是避免过度设计和开发,以及避免浪费时间和资源在不必要的功能上。根据这个原则,开发人员应该专注于当前需要解决的问题,而不是预测未来可能出现的需求。
单一职责原则SRP
单一职责原则(Single Responsibility Principle)指出一个类或模块应该有且只有一个引起它变化的原因。简而言之,单一职责原则要求一个类或模块只负责一项职责或功能。
开放封闭原则OCP
开闭原则(Open-Closed Principle,OCP)指出软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
对扩展开放:当需要添加新功能时,应该尽可能地开放类、模块、函数等的扩展点,通过扩展点给使用者增加新功能的机会。
对修改关闭:当需要修改现有功能时,应该尽可能地关闭类、模块、函数等的修改点,尽量通过扩展的方式来变更功能,而不是修改原有功能。
里氏替换原则 LSP
里氏替换原则(Liskov Substitution Principle,LSP)是由Barbara Liskov提出。LSP的核心思想是,任何基类(父类)可以被其子类替换,而不会影响程序的正确性。
现在总结下,一个组件要想满足里氏替换原则,必须满足以下条件:
- 子组件的输入不能比父组件严格:
子组件必须支持父组件所有属性,可以多,但不能少;
同一个属性子组件要允许更宽泛的格式,比如父组件某属性只支持String格式,子组件可以支持String、Number等多种情况,但不能反过来
同一个属性子组件允许更宽泛的校验,比如父组件要求某属性为1-10,子组件可以要求0-100,但是不能反过来
- 子组件的输出不能比父组件宽松:
比如父组件的某个method返回值为String和Number,子组件可以只返回Number格式,但是不能返回Array格式
父组件某个method返回数据范围为1-9,子组件不能返回0-10的数据,但可以返回2-8的数据
这里将"组件"换成"类"等其他概念都是相通的,只是后端可能经常使用类的概念,前端更多使用组件这个概念。
接口隔离原则 ISP
接口隔离原则(Interface Segregation Principle,ISP),客户端不应该依赖它不需要的接口; 类间的依赖关系应该建立在最小的接口上。
依赖倒置原则 DIP
依赖倒置原则(Dependency Inversion Principle)是指高层模块不应该依赖于低层模块的具体实现,而应该依赖于抽象,抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
高内聚低耦合
高内聚低耦合是判断软件设计好坏的一个标准,它强调类应该具有高内聚性和低耦合性。具体来说,高内聚意味着将相关代码组织在一起,以便它们可以共同完成一个任务或实现一个功能;而低耦合则意味着将模块之间的依赖关系降至最低,以便更容易进行维护和修改。
高内聚低耦合是一个非常重要的软件设计原则,通常项目中最难修改的问题都是因为强耦合造成的,最经典的就是修改一个模块的bug导致其他模块引发了N个新的bug,让人非常的痛苦。
高内聚描述的是模块内部的紧密关系的,一个模块内部的子模块应该是为了共同完成一个任务而被组织在一起的,他们之间应该有很强的逻辑相关性。
低耦合是描述模块之间的依赖关系的,模块之间的耦合有很多种方式,比如模块之间互相调用方法、依赖共同的全局数据,都会造成模块之间互相影响。
