searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

漫谈软件开发

2023-05-10 01:20:15
10
0

1.   需求篇  

世界是个因果关系的世界, 任何现象的产生都是有原因的。软件也不是凭空产生的,而是由需求触发的。比如客户需求,运维需求,个人需求等。

从某种程度来说,这个世界的进步也是由需求驱动的。因为只有需求才会改变现状。所以开发人员不要抗拒需求,有些高质量的需求确实能开拓视野,

让人眼前一亮。然而现实中有很多模糊不清的需求,如果按照自己的理解交付给客户,最后翻车的案例比比皆是。

比如客户说我需要做一个跟淘宝一样的网站,首页看上去很美。这种主观性非常强的需求对开发人员来说简直是巨坑。

需求一定是要有验收标准的,标准是可衡量的checklist。

还有一些需求直接附带了解决方案,比如之前有一个cdn客户直接把lua脚本提供给我们,说按照脚本里面的代码逻辑处理就可以了。

这种需求也是个坑。1.需求没有完整描述,测试人员编写测试用例比较困难,验收标准也不明确。2.客户提供的解决方案不一定是最优的和合理的。我们肯定要评估在实现中如何落地。

有时候需要透过需求琢磨客户提出的原因是什么,需要理解需求的背景,才能跟客户产生共鸣。需求是客户希望获取的价值服务,但他描述的不一定准确。

注意点:

1. 收到需求时先不要立即开发,需要针对需求中模糊的地方标记进行二次确认。

2. 客户需求一般列出来的都是功能性需求,一些非功能性隐性需求也不能忽略。比如性能,可用性,可维护性,容错性,数据完整性,可扩展性等。

 

 

2. 设计篇

软件开发的本质其实是将客户提出的可重复执行的业务流程以代码方式呈现,以符合预期的方式一遍遍重放。
设想一下业务流程没有变化,每个项目都是一锤子买卖,代码永远不会更改会有什么后果?

1.社会陷入僵化停滞,缺少活力。如同行尸走肉一般。

2.没有那么多的需求,供给会过剩。不需要那么多的码农了。最终结果就是开放填空题:码农的尽头是( )

幸好实际生活中客户面对的环境在随时变化,需求也不断。比如发现新的商机,竞争对手有的功能也需要有。

如果软件能以最小化修改的代价支撑业务灵活发展,就是软件创造的最大价值。

 

 如何保证需求变更时,软件修改范围最小化呢?

  先举两个例子。

  1. 我曾经接手一个别人写的函数5000多行。当时刚毕业没多久,之前在学校里写了200多行代码就沾沾自喜。看到这么大的函数,
    第一感觉是崇拜,然后花了2个星期才拜读完。代码一堆的if-else-do,还有大量goto。读完以后盲目自信,觉得工作中的挑战也不过如此嘛。
    直到有一天需求变更,需要修改这个大函数,我傻眼了。怎么改都改不对,一个bug解决了,十个bug冒出来了。程序还夺命连环core。经历了这次
    毒打以后,我明白一个道理:写大函数的人有病,崇拜写大函数的人更是病的不轻。

  2. 某个项目中,需要从数据源拉取数据进行业务处理。对接数据源为消息队列,向A队列用唯一message_id标识查询,从B队列根据message_id获取查询结果。当时实现时,把查询数据的逻辑跟业务处理逻辑混合到一起了。后来另外一个客户需要一样的业务流程,只是数据源改成从mysql数据库中查询数据,这种情况该如何处理?
    很多人第一反应估计是把原来的业务处理函数复制粘贴一份,函数内部再把查询数据部分改成mysql。毕竟是拷贝粘贴工程师嘛。
    冲动是魔鬼,冷静思考后我们把查询数据封装为一个通用的查询接口,在业务处理函数里面调用接口得到查询结果数据。
    后续又有新的需求,数据源改成oracle,业务处理函数内部也没有做任何改动。

上面两个例子比较有代表性。

第一个例子是典型的代码膨胀,写的是流水账代码。越到后面修改越难,内部分支交错,如同一团乱麻。

软件之所以叫软件,重点体现在软上面,相对硬件来说可以灵活修改。戏剧性的是:把软件写成了硬件,很多团队反而表现的很硬气。沟通需求时,一句“改不了”直接就怼回来了。

 

小朋友玩的拼积木游戏,积木只有基本的几种图案,但可以拼出很多造型,可以拼出房子,城堡,飞机等。

linux的命令都是小而美,通过简单的组合搭配成强大的功能。

这些都是设计软件时可以借鉴的。

函数实现的功能越多,支持的场景就越多,后续变动的概率就越大。

不但函数自身要修改,所有引用函数的地方都需要被迫修改。相反,实现的功能越专一,引起变动的因素就越小,就越稳定。

稳定创造价值,只有稳定,才能被复用的最多,修改的最少。职责单一才能提升效率。

现实中很多场景也是按照功能划分的:理发去理发店,饿了去饭店。即使是商场也分了很多功能区,一楼卖鞋子,二楼卖衣服,三楼饮食区,四楼影院。

超市货架也是按照商品属性划分的,如果没有划分,全都一个杂货铺混在一起,买个东西找一上午买家卖家都疯了。

 

 

针对第二个例子,本质上是将经常变化的查询逻辑封装为一个稳定的接口,将业务流程依赖具体细节改为依赖接口,只要接口稳定,实现的细节可以千变万化。

这里解释一下什么是依赖关系? 词典解释为:依靠别人或事物而不能自立或自给称为依赖。大白话就是I need you。

人是社会性动物,不可能脱离环境独立生存,一个人也不可能把360行全干了。同样软件也需要跟上下游有交互,不可能独立存在。

只要用到别人提供的服务,就对别人产生了依赖。

这里举个现实中的例子说明什么是依赖细节,什么是依赖接口。

很多人上下班选择公交出行,比如乘坐2路公交车,公交车把你从家送到公司楼下,你享受到服务,对公交车产生了依赖。

通常一个线路由几十辆公交车组成,它们共同点是都执行相同的线路。你坐上公交车是因为显示的是2路,而不是因为车牌号是xxx才坐。

换句话说,你虽然对公交产生依赖,但你依赖的是线路(接口),而不是依赖车牌号为XXX的具体的公交车(细节)。

如果说你非常偏好车牌号为xxx的2路车,那么有可能你等一上午都等不来。比如车堵在路上了,司机生病了。

相对于单辆公交车出状况,整个线路完全停摆概率非常低。只有建立稳定的依赖关系,才不会被细节变动所左右。

 

我们其实生活在一个接口的世界。

公司招聘发布的职位要求就是一个接口,公司发布招聘时并不知道自己最终招聘的是谁,但只要符合条件,任何人都可以。即使人员离职,只要新招一个符合条件的人进来,公司运转不受任何影响。

各种通信协议也是接口。比如tcp/ip协议,只要遵守协议约定,各种不同通信工具都可以无障碍通信,比如手机跟电脑,电脑跟平板等。

国家机构的设定其实也是一种接口组织。不管领导人选如何轮换,整个国家的运转也不受影响。

国家之间签订贸易协定也是一种接口依赖,并不会因为当时签署协议的某个人职位变动(退休),而导致协议失效。

各种例子不胜枚举。

 

既然接口如此重要,那么什么情况下需要设定接口? 

当对另外一方产生依赖的时候,就需要设定接口标准。小到一个函数调用另一个函数,大到一个产品集成另一个产品。

 

接口由谁来设定?

既然对另一方产生依赖,相当于是消费者,顾客就是上帝,接口自然就需要由消费者来设定。这点非常重要。虽然我

依赖你提供的服务,但你服务必须满足我的标准。这样一方面不会被对方绑架,跟随对方连锁变动。毕竟写代码最怕

别人修改自己也跟着改。另一方面也增加了选择的自由度。只要满足自己制定的标准,可以有多种选择。

当结局变成没得选,就是重度依赖某个具体的实体,被别人卡脖子。国家如此个人亦如此。

 

接口如何设定?

接口是一个双方都能接受的契约,它只重结果,不约定具体实现细节。条条大路通罗马。具体走哪条路是实现细节,只要最终走到罗马就行。

 

以上讲述了功能划分和接口设计的必要性,是维持软件最小化改动的基础。基础不牢地动山摇。只有基础稳定了,在此基础上搭建的业务流程

才保持稳定,最终达到业务流程的可复用。仔细想一想,各种框架本质上都是实现流程的复用。

 

3. 实现篇

什么方法可以确保写出来的代码又快逻辑又清晰?

答案是:写空函数。空函数是只有声明没有实现的函数。每个空函数实现流程中的一部分独立的业务逻辑。

这样做的好处是大大的。

  1. 一个空函数代表一段代码逻辑,相当于是一个接口。可以尽快的模拟流程过程,并发现其中的卡点,注意力只集中在最关键的部分。
  2. 增强代码可读性。函数名称相当于自注释,对实现的一段代码进行注释。
  3. 减轻脑力劳动。一旦流程固定下来,后续注意力只集中在实现一个个空函数就可以了。不需要再考虑函数之间的联系了。

 

什么方法可以确保在需求变更时对现有的结构改动最小?

答案是:封装变化。将需要修改的地方变成一个接口,这样就可以把变化封装在接口下面。后续即使有新的变动,原有引用接口的地方也不需要做任何变动。

为什么要这么做呢?因为变更跟家暴一样,只有零次和无数次。某个地方发生变更,后续很大概率还会发生变动。

设计模式讲的核心内容其实就是封装变化,在维持原有代码结构的基础上,将变化隔离最小化。

0条评论
0 / 1000
星空赶路者
2文章数
0粉丝数
星空赶路者
2 文章 | 0 粉丝
星空赶路者
2文章数
0粉丝数
星空赶路者
2 文章 | 0 粉丝
原创

漫谈软件开发

2023-05-10 01:20:15
10
0

1.   需求篇  

世界是个因果关系的世界, 任何现象的产生都是有原因的。软件也不是凭空产生的,而是由需求触发的。比如客户需求,运维需求,个人需求等。

从某种程度来说,这个世界的进步也是由需求驱动的。因为只有需求才会改变现状。所以开发人员不要抗拒需求,有些高质量的需求确实能开拓视野,

让人眼前一亮。然而现实中有很多模糊不清的需求,如果按照自己的理解交付给客户,最后翻车的案例比比皆是。

比如客户说我需要做一个跟淘宝一样的网站,首页看上去很美。这种主观性非常强的需求对开发人员来说简直是巨坑。

需求一定是要有验收标准的,标准是可衡量的checklist。

还有一些需求直接附带了解决方案,比如之前有一个cdn客户直接把lua脚本提供给我们,说按照脚本里面的代码逻辑处理就可以了。

这种需求也是个坑。1.需求没有完整描述,测试人员编写测试用例比较困难,验收标准也不明确。2.客户提供的解决方案不一定是最优的和合理的。我们肯定要评估在实现中如何落地。

有时候需要透过需求琢磨客户提出的原因是什么,需要理解需求的背景,才能跟客户产生共鸣。需求是客户希望获取的价值服务,但他描述的不一定准确。

注意点:

1. 收到需求时先不要立即开发,需要针对需求中模糊的地方标记进行二次确认。

2. 客户需求一般列出来的都是功能性需求,一些非功能性隐性需求也不能忽略。比如性能,可用性,可维护性,容错性,数据完整性,可扩展性等。

 

 

2. 设计篇

软件开发的本质其实是将客户提出的可重复执行的业务流程以代码方式呈现,以符合预期的方式一遍遍重放。
设想一下业务流程没有变化,每个项目都是一锤子买卖,代码永远不会更改会有什么后果?

1.社会陷入僵化停滞,缺少活力。如同行尸走肉一般。

2.没有那么多的需求,供给会过剩。不需要那么多的码农了。最终结果就是开放填空题:码农的尽头是( )

幸好实际生活中客户面对的环境在随时变化,需求也不断。比如发现新的商机,竞争对手有的功能也需要有。

如果软件能以最小化修改的代价支撑业务灵活发展,就是软件创造的最大价值。

 

 如何保证需求变更时,软件修改范围最小化呢?

  先举两个例子。

  1. 我曾经接手一个别人写的函数5000多行。当时刚毕业没多久,之前在学校里写了200多行代码就沾沾自喜。看到这么大的函数,
    第一感觉是崇拜,然后花了2个星期才拜读完。代码一堆的if-else-do,还有大量goto。读完以后盲目自信,觉得工作中的挑战也不过如此嘛。
    直到有一天需求变更,需要修改这个大函数,我傻眼了。怎么改都改不对,一个bug解决了,十个bug冒出来了。程序还夺命连环core。经历了这次
    毒打以后,我明白一个道理:写大函数的人有病,崇拜写大函数的人更是病的不轻。

  2. 某个项目中,需要从数据源拉取数据进行业务处理。对接数据源为消息队列,向A队列用唯一message_id标识查询,从B队列根据message_id获取查询结果。当时实现时,把查询数据的逻辑跟业务处理逻辑混合到一起了。后来另外一个客户需要一样的业务流程,只是数据源改成从mysql数据库中查询数据,这种情况该如何处理?
    很多人第一反应估计是把原来的业务处理函数复制粘贴一份,函数内部再把查询数据部分改成mysql。毕竟是拷贝粘贴工程师嘛。
    冲动是魔鬼,冷静思考后我们把查询数据封装为一个通用的查询接口,在业务处理函数里面调用接口得到查询结果数据。
    后续又有新的需求,数据源改成oracle,业务处理函数内部也没有做任何改动。

上面两个例子比较有代表性。

第一个例子是典型的代码膨胀,写的是流水账代码。越到后面修改越难,内部分支交错,如同一团乱麻。

软件之所以叫软件,重点体现在软上面,相对硬件来说可以灵活修改。戏剧性的是:把软件写成了硬件,很多团队反而表现的很硬气。沟通需求时,一句“改不了”直接就怼回来了。

 

小朋友玩的拼积木游戏,积木只有基本的几种图案,但可以拼出很多造型,可以拼出房子,城堡,飞机等。

linux的命令都是小而美,通过简单的组合搭配成强大的功能。

这些都是设计软件时可以借鉴的。

函数实现的功能越多,支持的场景就越多,后续变动的概率就越大。

不但函数自身要修改,所有引用函数的地方都需要被迫修改。相反,实现的功能越专一,引起变动的因素就越小,就越稳定。

稳定创造价值,只有稳定,才能被复用的最多,修改的最少。职责单一才能提升效率。

现实中很多场景也是按照功能划分的:理发去理发店,饿了去饭店。即使是商场也分了很多功能区,一楼卖鞋子,二楼卖衣服,三楼饮食区,四楼影院。

超市货架也是按照商品属性划分的,如果没有划分,全都一个杂货铺混在一起,买个东西找一上午买家卖家都疯了。

 

 

针对第二个例子,本质上是将经常变化的查询逻辑封装为一个稳定的接口,将业务流程依赖具体细节改为依赖接口,只要接口稳定,实现的细节可以千变万化。

这里解释一下什么是依赖关系? 词典解释为:依靠别人或事物而不能自立或自给称为依赖。大白话就是I need you。

人是社会性动物,不可能脱离环境独立生存,一个人也不可能把360行全干了。同样软件也需要跟上下游有交互,不可能独立存在。

只要用到别人提供的服务,就对别人产生了依赖。

这里举个现实中的例子说明什么是依赖细节,什么是依赖接口。

很多人上下班选择公交出行,比如乘坐2路公交车,公交车把你从家送到公司楼下,你享受到服务,对公交车产生了依赖。

通常一个线路由几十辆公交车组成,它们共同点是都执行相同的线路。你坐上公交车是因为显示的是2路,而不是因为车牌号是xxx才坐。

换句话说,你虽然对公交产生依赖,但你依赖的是线路(接口),而不是依赖车牌号为XXX的具体的公交车(细节)。

如果说你非常偏好车牌号为xxx的2路车,那么有可能你等一上午都等不来。比如车堵在路上了,司机生病了。

相对于单辆公交车出状况,整个线路完全停摆概率非常低。只有建立稳定的依赖关系,才不会被细节变动所左右。

 

我们其实生活在一个接口的世界。

公司招聘发布的职位要求就是一个接口,公司发布招聘时并不知道自己最终招聘的是谁,但只要符合条件,任何人都可以。即使人员离职,只要新招一个符合条件的人进来,公司运转不受任何影响。

各种通信协议也是接口。比如tcp/ip协议,只要遵守协议约定,各种不同通信工具都可以无障碍通信,比如手机跟电脑,电脑跟平板等。

国家机构的设定其实也是一种接口组织。不管领导人选如何轮换,整个国家的运转也不受影响。

国家之间签订贸易协定也是一种接口依赖,并不会因为当时签署协议的某个人职位变动(退休),而导致协议失效。

各种例子不胜枚举。

 

既然接口如此重要,那么什么情况下需要设定接口? 

当对另外一方产生依赖的时候,就需要设定接口标准。小到一个函数调用另一个函数,大到一个产品集成另一个产品。

 

接口由谁来设定?

既然对另一方产生依赖,相当于是消费者,顾客就是上帝,接口自然就需要由消费者来设定。这点非常重要。虽然我

依赖你提供的服务,但你服务必须满足我的标准。这样一方面不会被对方绑架,跟随对方连锁变动。毕竟写代码最怕

别人修改自己也跟着改。另一方面也增加了选择的自由度。只要满足自己制定的标准,可以有多种选择。

当结局变成没得选,就是重度依赖某个具体的实体,被别人卡脖子。国家如此个人亦如此。

 

接口如何设定?

接口是一个双方都能接受的契约,它只重结果,不约定具体实现细节。条条大路通罗马。具体走哪条路是实现细节,只要最终走到罗马就行。

 

以上讲述了功能划分和接口设计的必要性,是维持软件最小化改动的基础。基础不牢地动山摇。只有基础稳定了,在此基础上搭建的业务流程

才保持稳定,最终达到业务流程的可复用。仔细想一想,各种框架本质上都是实现流程的复用。

 

3. 实现篇

什么方法可以确保写出来的代码又快逻辑又清晰?

答案是:写空函数。空函数是只有声明没有实现的函数。每个空函数实现流程中的一部分独立的业务逻辑。

这样做的好处是大大的。

  1. 一个空函数代表一段代码逻辑,相当于是一个接口。可以尽快的模拟流程过程,并发现其中的卡点,注意力只集中在最关键的部分。
  2. 增强代码可读性。函数名称相当于自注释,对实现的一段代码进行注释。
  3. 减轻脑力劳动。一旦流程固定下来,后续注意力只集中在实现一个个空函数就可以了。不需要再考虑函数之间的联系了。

 

什么方法可以确保在需求变更时对现有的结构改动最小?

答案是:封装变化。将需要修改的地方变成一个接口,这样就可以把变化封装在接口下面。后续即使有新的变动,原有引用接口的地方也不需要做任何变动。

为什么要这么做呢?因为变更跟家暴一样,只有零次和无数次。某个地方发生变更,后续很大概率还会发生变动。

设计模式讲的核心内容其实就是封装变化,在维持原有代码结构的基础上,将变化隔离最小化。

文章来自个人专栏
漫谈设计
2 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0