2019独角兽企业重金招聘Python工程师标准>>>

有没有想过这个问题,为什么我们的代码难以维护,为什么我们偏向于开发全新的系统,却不愿意改造现有系统,甚至是我们自己开发的系统。这个想法突然从我脑钻了出来,然后我思考了大概十分钟。

我认为我们系统难以维护的原因主要有三个:

一. 系统抽象级别不够

其实很多时候我们太急于实现功能,导致代码太过于具体,没有得到有效的抽象或者抽象层次不够。抽象层次不够会导致代码难以复用,难以修改,难以阅读。

《程序员修炼之道》中有一节讲元数据的,其中提到系统应该为一般逻辑编程,而将特殊逻辑作为元数据写成配置项。

1.1 代码封装层次

比如我要实现一个actionSheet,根据系统状态不同展示不同的actionSheet,并且根据用户最终选择的action,执行响应的操作。 这是一个在移动端非常常见的交互行为。

看一下我们不同的抽象级别的实现:

function handleEvent(e) {// 使用者只需要构造mapper即可,无需关心其他逻辑const buttonMapper = {"2": [{text: "action one",onClick: this.onButtonClick.bind(this, employeeStatus, username)},{text: "action two",onClick: this.props.leaveJob}],"3": [{text: "action three",onClick: this.props.leaveJob}]]};return showActionSheet(buttonMapper, e.target.value)
}// 系统抽象(将actionSheet抽象)
function showActionSheet(buttonMapper, idx = 0, title = "", cancelButton = "取消") {const actions = buttonMapper[idx];const BUTTONS = actions.map(q => q.text);actionSheet({title,cancelButton, //取消按钮文本otherButtons: BUTTONS,onSuccess: ({ buttonIndex }) => {actions[buttonIndex].onClick();}});
}

如果代码抽象层次太低,会导致代码很难复用,难以阅读和维护。 比如下面的代码:

function handleEvent(e) {const BUTTONS = isTrue? ["action one","action two"]: ["action three"];actionSheet({title:'title',cancelButton: "取消", //取消按钮文本otherButtons: BUTTONS,onSuccess: ({ buttonIndex }) => {if (isTrue) {if (buttonIndex === 0) {// xxxxxx} else if (buttonIndex === 1) {// xxxxx}} else {if (buttonIndex === 0) {// xxxxx}}}});
}   

1.2 业务思维层次

比如有这样一个需求。 后台给定一个JSON,结构如下:

{history: [{}, {}],baseInfo: {name: '张三', dept: '工程部'},bonus: {a: '100',b: '210'}
}

需要将history以 timeline形式展示

baseInfo按照form表单形式全部展示,bonus按照box形式展示。

这种需求非常常见。

当时我做这个需求的时候想法是这样的:

1. 拆分组件。

我需要写一个timeLine组件。使用方法大概是这样:

<Timeline><Timeline.Item>创建服务现场 2015-09-01</Timeline.Item><Timeline.Item>初步排除网络异常 2015-09-01</Timeline.Item><Timeline.Item>技术测试异常 2015-09-01</Timeline.Item><Timeline.Item>网络异常正在修复 2015-09-01</Timeline.Item>
</Timeline>

我还需要一个form组件,使用方式大概是这样的:

const mapper = {name: "姓名",dept: "部门",};<TextForm data={baseInfo} labelMapper={mapper} />

我还需要一个box组件,用法如下:

       <Box cols={4}><BoxItemtext={d}suffix="元"linkText="贡献奖"ceiling={9999}/>...<BoxItemsuffix="元"text={c}linkText="全勤奖"ceiling={9999}/></Box>

2. 在容器组件中拼装。

以timeline为例。

           <TimeLine>{history.map((item) => {       return (<TimeLineItemkey={item.key}     date={item.effectTime || ""}color={item.key > 0 ? "gray" : "blue"}>{item.title}</TimeLineItem>);})}</TimeLine>

后期这种抽象方式还是不够,还可以进一步抽象,经过进一步思考,我采用如下方式抽象。

以timeLine实现为例:

const renderItem = item = <Timeline.Item key={item.key}            date={item.effectTime || ""}color={item.key > 0 ? "gray" : "blue"}
>
{item.title}
</ Timeline.Item>const renderList = ({children}) = <Timeline>
{children}
</ Timeline>const renderTimeLine = compose(renderList ,map(renderItem))renderTimeLine(data.history)

本质上渲染一个timeline组件由两部分组成,渲染外层wrapper,渲染里层items。 渲染items又可以看作渲染items执行n遍。 于是问题简化为 1.生成wrapper 2. 生成item。  经过这样抽象,你会发现上面例子的baseInfo和 bonus 其实也可以抽象为上面的“模型”。

二. 系统状态组织混乱

其实将系统抽象为状态,从状态映射视图也是对业务进行的抽象。有一个名词叫有限状态机,其实系统中的状态通常都是有限的,系统总是在有限状态中的一个节点。也就是说系统只是状态在时间节点上的分布。前端有很多状态管理容器,有名的有redux, mobx, vuex,statex 等。拿redux来讲,redux 中最核心的reducer, 其实就是reduce,只不过reduce 遍历的是给定的list。 而redux 遍历的是随时间变化的action list。

系统中状态混乱,彼此之间关联是造成代码难以维护的另一个重要原因。如何组织好系统状态是重要工作,我们不可能依靠某一个工具就可以很好组织我们的app state。状态管理有很多问题需要解决,比如状态如何组织,如何共享,如何保证不污染, 如何优雅订阅状态变更,如何优雅地修改状态等等。

有这么多问题,我们来看下redux(包括react-redux)对上面这些问题做了哪些努力。

1. 状态如何组织

redux是单一数据源,对于业务复杂的,可以写多个reducers,然后combine reducers。

2. 如何共享

redux每次store改变, 都会导致provider 中store变更,从而导致子组件刷新。然后子组件订阅单一数据源的部分key。

3. 如何优雅订阅状态变更

如上

4. 如何保证状态不相互污染。

redux通过immutable方式修改state。

5. 如何优雅地修改状态

redux 通过diapatch action 修改 state。 dispatch一个 action,相当于往actions数组里面push一个action。然后reduce 当前应用状态的state。 通过actions 就可以还原,回退,从而还原“案发现场”

上面这些只是说了redux对状态管理做的努力,再好的工具,如果使用者本身技能不够好,也会把工具用得一团糟。对于使用者来说,一定要明白引入工具解决了什么问题,这样才可以更好的使用工具。对于app state的管理,并不是说引入了redux,就要将应用全部state都放在redux中管理。另外也不是所有应用都适合状态管理框架(应用复杂度要和框架复杂度匹配)。因此这就需要我们对app state有一个完整的理解,恰当地管理app state。一般而言,app state分为两部分,一部分是需要共享的状态,另一部分是组件独享的状态。如何区分比较简单,关键是使用者有去区分的”意识“。 而redux做状态共享非常方便,但是独享的状态使用redux就没必要了。对于完全不需要共享的状态,可以维护在上层container组件中,将这些脏活累活讲给container组件,然后将独享状态分发,保证下层组件的**纯粹性**。

不管是独享还是共享希望都可以从上面提到的几点去思考。

> 在这里,并不其他组件没有使用当前组件状态就不是共享状态,这需要你对应用未来的理解。因为可能出现这个状态本身就应该是共享的,只是目前暂时不共享的情况。

三. 对于非自己开发的功能不熟悉,不明白功能的具体业务场景。

对于这种,从代码 层次上是无法缓解的。那么就必须从文档和注释上下功夫。文档的话我觉得对于一个需求应该有完善的产品文档,UI设计文档和开发文档。  对于一些业务最好可以给一个产品文档和UI设计文档的链接。 因此推荐将业务讨论的过程通过外部文档记录,并将记录过程于文档结合,方便相关人员查看。

// prd: http://www.xxx.com/1233423x/index.html
// sketch: http://www.xxx.com/1233423x/index.htmlclass PageA {xxxxxx
}

适当增加注释,还原当初业务场景:

function renderHistory() {// 由于历史遗留,需要处理旧数据,因此这里需要做兼容处理// xxxx
}

转载于:https://my.oschina.net/wanjubang/blog/1580050

为什么我们的代码难以维护(草稿)相关推荐

  1. 维护旧项目_为什么您的旧版软件难以维护-以及如何处理。

    维护旧项目 Believe it or not, some organizations still rely on legacy software to carry out operations ev ...

  2. SAP QM初阶-执行事务代码QP02维护检验计划的时候不能为检验特性指派取样策略

    SAP QM初阶-执行事务代码QP02维护检验计划的时候不能为检验特性指派取样策略 执行事务代码QP02去修改检验计划组54,因为其检验特性item里没有指派取样策略. 发现Sampling proc ...

  3. SAP QM 执行事务代码QS51维护使用决策的选择集,系统报错 – Transaction no longer valid for catalog ‘3’ -

    SAP QM 执行事务代码QS51维护使用决策的选择集,系统报错 – Transaction no longer valid for catalog '3' - 执行事务代码QS51,试图为工厂NMD ...

  4. SAP MM初阶事务代码MEK1维护PB00价格

    SAP MM初阶事务代码MEK1维护PB00价格 MM模块里的采购价格,比如条件类型PB00的价格,除了可以在ME11里维护以外,还可以在事务代码MEK1里维护. 执行事务代码MEK1,条件类型PB0 ...

  5. 城市筛选数据(根据2020年度全国统计用区划代码和城乡划分代码更新维护的标准)

    根据2020年度全国统计用区划代码和城乡划分代码更新维护的标准,整理的城市联动筛选数据: /* 根据2020年度全国统计用区划代码和城乡划分代码更新维护的标准 */ var cityList = [{ ...

  6. 代码的维护成本远远大于开发成本

    一般来说,功能需求决定业务构架.非功能需求决定技术构架,变化案例决定构架的范围. 软件设计工作只有基于用户需求,立足于可行的技术才有可能成功. 算法优化及负载均衡是性能优化的方向. 代码的维护成本远远 ...

  7. 如何写出难以维护的代码--代码命名

    New Uses For Names For Baby Buy a copy of a baby naming book and you'll never be at a loss for varia ...

  8. Java代码的维护与更新,Java常用的规则引擎,让你变动的代码更加容易维护

    Java常用的规则引擎,让你变动的代码更加容易维护 Java常用的规则引擎,让你变动的代码更加容易维护 在本文中,我们将介绍Java中一些最受欢迎的规则引擎. 规则引擎由推理引擎发展而来,是一种嵌入在 ...

  9. 如何让你的代码好维护

    先说说我们不喜欢的代码长什么样子, 大函数,超过500行,甚至超过1000行; 大量的hard coding; if和else if中有明显的条件关联性; 注释和代码逻辑不符合,函数名与功能不符合; ...

最新文章

  1. HTML 5 Web Workers
  2. [JSP]解决Maven创建项目失败
  3. JavaScript / HTML5中的音效
  4. 理解VMware虚拟机下网络连接的三种模式
  5. hive 数据存储格式详解
  6. Linux 条件变量使用细节(为何调用 pthread_cond_wait 前加锁,函数内部解锁,返回时又加锁)
  7. Windows设置HTML,windows怎么添加静态路由
  8. getdate 日期间隔_日期getDate()方法以及JavaScript中的示例
  9. 饭圈出征?《流浪地球》影迷给豆瓣App打一星:来啊互相伤害
  10. 解除ASP.net上传文件大小的限制
  11. 经纬张颖「炮轰」扫码点餐;淘宝特价版给拼多多送芒果;Firefox 87.0 发布|极客头条...
  12. 为什么派生的子类报错不能实例化抽象类_C# 接口与抽象类实例分析
  13. 理解 RESTful
  14. 怎么锁定电脑屏幕_锁定流行趋势,信霆为你盘点3C数码配件中的人气单品
  15. checkbox未赋值时获取value是on_【漏洞分析】关于mysqlconnectorjava连接时的反序列化...
  16. 有一些四位数,百位数字都是3,十位数字都是6,并且它们既能被2整除,又能被3整除,求这样的四位数中最大的和最小的两数各是几?
  17. 客户端无法远程连接服务器的问题
  18. PostgreSQL使用pgAdmin3不能编辑表里的数据
  19. Access violation at address 77106D4E in module 'ntdll.dll'. Write of address 004051A5.
  20. html截取一部分作为图片,怎么截取文档的一部分

热门文章

  1. 不要再问芝士和奶酪有什么区别了!一次解释清楚
  2. 使Android Home键,Power键等按钮失效
  3. 16数码管静态显示(74hc_595)
  4. 俄罗斯方块的简单实现
  5. 《“爱”是如此的容易...》
  6. MacTeX新手学习笔记
  7. 二维码(微信二维码)缓存问题的解决方法
  8. 用python画皮卡丘-用python画一只可爱的皮卡丘
  9. 怎么查找无线的dns服务器,无线找不到服务器dns地址
  10. bat 使用 7z 压缩文件夹