专栏名称: 程序猿
本微信公众号:imkuqin,为程序员提供最新最全的编程学习资料的查询。目前已经开通PHP、C/C++函数库、.NET Framework类库、J2SE API查询功能。
目录
相关文章推荐
程序员小灰  ·  2024 年 09 ... ·  昨天  
程序员小灰  ·  《黑神话:悟空》,我终于通关了! ·  14 小时前  
程序员的那些事  ·  造福无数打工人,世界第 7 ... ·  5 天前  
程序猿  ·  Python 势不可挡:直逼 Java 的 ... ·  1 周前  
51好读  ›  专栏  ›  程序猿

是什么阻碍了代码的重用?问题是否应该只解决一次即可?

程序猿  · 公众号  · 程序员  · 2016-11-20 22:22

正文

来自:知乎

链接:https://www.zhihu.com/question/21011591(点击尾部阅读原文前往,查看更多答案)


刚学编程的时候有种想法,认为难题应该只解决一次。

但渐渐接触多了前端开发,经常要重复编写代码,特别是生成页面时。

是什么原因导致了代码无法重用呢?


sapjax

为什么还要“经常要重复编写代码”呢?,因为问题本身并不是同一个问题了,如果是同一个问题,即使不做抽象,CTRL+C/CTRL+V大法就解决了。


举前端的例子来说,如果在同样的基础库/框架下,一个UI控件的外观和交互都一致的情况下,代码就可以直接拿来复用。业务逻辑的模块也同理,假设业务逻辑真的一致,直接copy过来也未尝不可。


试想一下,假设你们前端团队决定使用Bootstrap或者Semantic UI作为UI库,你们说服了产品经理和设计师都去阅读了Bootstrap/Semantic UI的文档,做出来的原型和视觉稿都是基于UI库已有的规范和控件,那么到了前端这里,在UI实现的工作上就变成了简单的拼装组合,并不需要重复去写UI控件的样式和交互,这将是多么和谐的一幅画面...


退一步说,产品经理和设计师都是有追求的,不甘于拿来和定制别人的东西,要做出自己风格化的设计,前端基于这些设计提取出一套类似BootStrap/Semantic UI的库供自己内部使用,后续有类似的需求,同样是简单的拼装组合,能做到这一步也是极好的。


然后实际中,情况是这样的:产品经理和设计师大多数时候并不知道“重用”是什么概念,原型和视觉元素大多数时候都是随意发挥,是拍脑袋拍出来的灵感集合,也从来没有一套设计规范,同一个元素出现十个地方就有可能有八种变形,交互规则也是千奇百怪。在这种情况下,前端要提取出通用元素,实现一套UI库就变成了不可能的任务。


“复用”这个观念不仅仅是程序员要思考的,更是产品经理和设计师需要注意的,如果他们给的东西本来就不具有复用性,程序员自然要“经常要重复编写代码”了。如果在原型和设计层面就已经抽象得很好了,开发的工作就会变得很简单,蚂蚁金服的ant.design 就很明白,所以他们叫做“一个UI设计语言”。


那么回到题主的问题,答案已经很明显了,是什么阻碍了代码的重用?




是设计狗和产品汪啊!


陈甫鸼编程话题优秀回答者 生长于闽,求学入秦,漂泊适燕…

因为复用并非无代价,而且代价往往还很高。


从工程上说,任何特性都不是无代价的。复用提供了解决一类问题的灵活性,而灵活性作为一种功能,同样有代价——正如过多地使用虚函数有性能损失,而过多地使用 interface 则一定程度上降低代码可读性。如果构建灵活性的基石皆有代价,那么我们不可能期望灵活性可以免费获得。而所有的问题解决之道本质上都一样:我们需要权衡每一个选择的好处和坏处,做出对我们现在的项目最有利的方案。举例来说:我们不会在项目最紧张的时候讨论把业务逻辑抽取出来做一个通用的框架,原因很简单:时间不够用。我们也不会在讨论怎么设计通用框架的时候过多地讨论我们具体项目的逻辑,因为追求通用性的设计目的导致我们不可能完全为某一个具体的业务优化。


所以从这个意义上来说,阻碍代码重用的最大原因,事实上来自项目自身:复用代码在绝大多数情况下,都不是一个项目的最终目的。对任何项目来说,唯一绝对存在的目的,是在指定的时间内完成客户给出的需求。当短期内完成功能的需求和复用发生冲突时,理智的项目管理者都不会选择将注意力放在复用上。当然,热衷于复用的程序员必然会以长远的好处为理由为复用辩护;但正如前面的规则指出的,这依然是一个工程上的选择问题,因而仍然需要折衷。在遇到问题时,总是先倒向某一个结果再试图解释,这不是折衷,而是预设立场,这恰恰是工程的大忌。


至于第二个问题,回答是:是的,对任何问题只解决一次是理想状态,但重复解决三到五次问题并非十恶不赦。客户关心的是我们能不能解决他们的问题,而不是能不能对任何问题都只解决一次。——不要把自己的需求误以为是用户的需求,这仍然是一个工程问题。


苏莉安程序员话题优秀回答者 年轻人,你想学编程吗?

过早优化是万恶之源。


编写代码时,对“可复用”和“扩展性”的考虑,都应限定在一个很小的范围内。否则的话,在当前版本多消耗的时间要远远大于未来复用代码时省下的时间。


从“编程艺术”的角度来讲,这种解决方法一点都不漂亮。每个人初学开发时都会有这种想法:要完美、要考虑周全、为了不存在的需求而编写一堆“预备代码”。但是,在经历过几个项目后总结一下,就会发现这种事是多么浪费时间和精力,当初预想的需求90%没有出现,出现的那部分也和设想大相径庭了。


冯东Linux、编程话题优秀回答者 Lua, Cocoa, OpenGL, Wi…

1、编写代码时,如果考虑今后的重用,工作量一般是只为当前情况考虑的三倍。(参见《人月神话》)


2、「考虑今后的重用」只能靠猜,一般都是错的。


3、读代码比写代码难,所以那些「只为当前情况考虑的」代码很少在问题稍稍发生变化的时候被改写成更通用的代码。


补充一下,原来的第三条写的太简短。第三条虽然再说一个困难的现状,但是它也同时说明这是正确的重用方式。就是每次写代码的时候,只为当前情况考虑。准确的说是只为迄今为止遇到过的情况考虑。这样一份代码会经历从专用到通用的过程。但是这个过程也是非常困难的。


悟网不欢

作为曾经对复用问题非常关心的前软件行业从业人员,把知乎的第一答献给这个问题。


我相信许多对复用抱怨的人,可能了误解的复用的含义。而那些强调局限性和折中的人,很可能本身已经拥有良好的复用功底,因此对他们来说,复用的程度更多取决于现实需要。因此这个问题对于开发老手来说或许不值一提,对于新人来说,却绝对重要,它在某种程度上决定了你作为一个程序员所能到达的技术高度。


直截了当的回答,在我看来,阻碍复用的因素只有一个:开发者自己。具体的说是开发者自己的习惯、思维方式和经验。软件设计是一项思维工作,工作的一个重点就是描述流程和划分模块。划分模块的目的,除了逻辑清晰化和便于分工之外,最重要的就是复用。只有把一大堆功能拆分成若干相对独立的小功能,才方便我们把小功能封装起来,随时随地被其它功能(模块)反复使用(调用),这本身就是复用。


从最原始的程序块上升到函数、类乃至设计模式和架构,是对程序功能一步步抽象和灵活描述的过程,也是提高复用性的过程。为什么我们需要架构师?就是因为架构师能够对整个程序,以及程序可能发生的变化做一概括性的描述,在此描述之内,可容纳程序所需的所有具体功能,并可对功能变化表现出适当的容忍性、灵活性和可扩展性。如果不考虑复用,功能稍有变化就要修改或复制大量代码,我们要灵活性和可扩展性有何用?要架构和架构师又有何用?


或许有人会说,我们开发的程序很小,根本不需要架构师,对复用没有那么高的要求,尽快满足客户的要求就好。这个说法的逻辑,如同磨刀一定会误了砍柴功一样,正确,但不合理。我见过泾渭分明的两类程序员,同样从几乎完全没有经验的应届毕业生开始,一类天天忙的喘不过气来,成果却不多。另外一类却越做越轻松,进而经常帮助别人解决问题。除了努力程度之外,是否具有抽象、复用的思维(当然这也可以归结为悟性),是他们在开发能力上的一大分野。


相信大家见过这样的优秀程序员,从一个项目转战到另外一个项目时优秀,从一种语言转战到另外一个语言的时候依然优秀,甚至从一个领域转战到另外一个领域时还是优秀。尽管这种优秀是相对的,但是主要原因在于他能把一个项目、语言、领域的经验复用到另外一个。这是更高层次的复用,而其基础依然来源于软件开发时复用思维的培养与实践经验。


那么,具体来说开发者自己的习惯、思维方式和经验是如何阻碍复用的呢?首先,如果一个开发者在编码之前根本不做任何“程序设计”方面的工作,一接到任务就开始埋头于功能的实现,那么别指望他的代码能有什么复用性,也别指望他能培养起什么复用的思维;其次,如果一个开发者不经常寻找相似模块之间的共性,不打算让自己的模块体现出更强的灵活性,他就没有复用能力,也无法建立复用的思维;最后,如果一个开发者没有足够的经验,他也很难确定复用的边界,很有可能引发过度设计或者放弃复用。


因此,作为开发者,抱怨用户是没有用的,抱怨需求分析师、项目经理或者架构师也是没有用的,交给你的模块在你手中,它的复用性只取决于你,你想让它复用性高一点,那就花点心思去分析和设计。如果你要偷懒,那也随你,只是不要把接口放在别人身上。如果你说你的工作实在太多了,根本没有时间考虑这方面的问题,那我除了对你报以深深的同情之外,只能说一句:同学,身体和职业前途为重。


那么,如何培养开发者的复用思维?就我的经验而言:

首先是严禁复制代码,绝不允许同一个项目或产品的成员复制其它成员(包括自己)的代码直接插入自己的程序,复制已有代码的愿望说明代码上有较大重复,已经构成了复用的条件,在时间允许的情况下,应该要求原开发人对代码进行封装,然后由你来调用。如果时间上来不及,最低限度的折中也是你把这段代码复制过来之后进行分装,然后调用;


其次是对设计过程的强调,新人在开发前,一定要有老手带他进行程序设计和后期的代码审核,这会花费老手的时间,但是回报绝对值得,设计过程培养的不仅仅是新手的复用思维,而是软件开发的基本功,这个基本功与对语言的熟悉程度和项目经验具有同等价值,长期看甚至过之;


最后是适当的交流与指导,实际上,复用问题是新人最容易遇到的第一个显著成长瓶颈,有经验的管理者或者同事很容易发现,在对开发语言和项目逐步熟悉之后,编码成为最不费脑子的工作,而如何避免让自己成为体力劳动者,或者说如何让自己的每一次劳动成果都获得尽量多的后续应用,即复用,开始检验新人的悟性。有悟性的人会自己琢磨、学习、尝试解决,没有悟性的人就开始苦恼。这个时候,就到了指导他们重温面向对象的设计、设计模式等知识的时间了,从这些老生常谈的知识,结合具体的项目实践,所能挖掘出来的复用思维和技术,可能超乎大部分计算机专业毕业生的想象。


事实上,为了解决复用设计问题,我曾经为公司的新人专门开过一门夜校课程,名字就叫“软件复用”。我向他们解释软件复用的极端重要意义,过程很简单,列出了某开源PHP框架对于MVC架构实现的核心代码,解释了短短几行代码所体现的多种设计模式与巨大灵活性。所有人都震惊了,许多人开始意识到编程并不是写代码,更是一种逻辑思维,如果把编程只局限于写代码的水平,那么“码农”的称谓当之无愧。而在这种逻辑思维中,抽象能力是核心,也是复用的基础。

通过大约10多个小时的夜校课程,我很难说显著的提高了他们的水平,但有一点值得欣慰:就是大多数人都接受了“高内聚、低耦合”的思想。众所周知,“高内聚、低耦合”只是模块划分的核心原则,但真正掌握了这个,就基本上迈进了软件复用的门槛,剩下的是实践和提高问题。


上面是从培养的角度来说。对于程序员新人的自我学习,如何提高复用水平,答案也很简单:思考、抽象与重构。首先思考模块划分问题,怎么划分才能尽可能的保证模块的独立性和灵活性,其次寻找相似模块之间的共性,如果找不到,说明抽象程度不够,再往上抽象;最后,舍得花时间重构代码。我完全同意前面大部分人所说的同样代码写到第三遍就会恶心的观点。如果你有时间写三遍,却没有时间重构后,让它被调用两遍,真的很难说到底是偷懒还是过于勤快。


当然复用总是有边界的,开发过程中总会遇到复用所带来的复杂性与时间和代码可读性之间的冲突。这个时候发挥作用的是经验,这个经验既包括复用技术方面的经验,也包括用户需求方面的经验,都需要积累。但是这个冲突不应成为阻碍复用设计的借口,复用首先应该是渗透入程序员血液中的思想,否则前人所作的一切工作,你自己完成的所有任务,理论界、技术界大牛所提供给你的一切工具、模式都是白费。在拥有这个思想,积累的一定的经验之后,你再说:在这个功能点或项目中,复用考虑的不够;或者,在那个功能点或项目中,过多考虑复用并不合适;因为blablabla……


恭喜你,这个时候你已经是一个合格的程序员了。


因为在我看来,复用从来没有该不该的问题,只有够不够和多不多的问题。


PS1. 以上所提“新人”均为普通大学(包括本科、专科和中专)毕业生,天才不在此列;


PS2. 本人并非高水平程序员,只是在培养新人方面有一些心得和体会,如答案不靠谱,请直接点击“没有帮助”,但是千万不要让我背上“误人子弟”的骂名,切记切记。


链接:https://www.zhihu.com/question/21011591(点击尾部阅读原文前往,查看更多答案)



本文编号2070,以后想阅读这篇文章直接输入2070即可。

●本文分类“项目管理搜索分类名可以获得相关文章。

●输入m可以获取到文章目录

本文内容的相关公众号推荐

程序员的那点事

算法与数据结构


更多推荐15个技术类公众微信

涵盖:程序人生、算法与数据结构、黑客技术与网络安全、大数据技术、前端开发、Java、Python、Web开发、安卓开发、iOS开发、C/C++、.NET、Linux、数据库、运维等。