阅读更多

4顶
0踩

非技术
引用
作者简介:Zm,爱生活,爱分享。近五年的实际开发经验,多个项目的积累与总结,在代码中经历太多的喜怒哀乐。
本文来自作者 Zm 在 GitChat 上分享 「日常开发与设计模式的那点事」。

【作者按】很多程序员不知道怎么组织代码、怎么提升效率、怎么提高代码的可维护性、可重用性、可扩展性、灵活性,写出来的代码一团糟,但这样一团糟的代码居然能正常运行。

这样的代码经历,你是否也似曾相识?

身边好多程序员都会有这样的一个经历,过个一年半载再去查看曾经写下的代码时,很吃惊的在想,这么糟糕的代码,真的是我以前写的吗?我居然能写出这么糟糕的代码!

而对于还在维护的代码,此时,会萌生一种去重构的想法,或者会有一种更好的方式去实现。此时,你与代码的爱恨情仇已经开始了……

本篇主要从六大基本原则说起,作为设计模式的引子,叙述六大基本原则和设计模式的关系,后续会一篇一个设计模式,详细介绍设计模式与日常开发。

基本的规范和约束

对于基本的规范和约束,我相信每个合格的团队都会有一套自己的玩意,一方面统一标准,增加可读性和可维护性,另一方面也方便离职后出现 Bug,后来者也能更快的去定位并解决问题。

杂乱无章的代码实现一个大功能,对于后来者去维护,无疑会亲切的问候各路祖宗。一个好的编码习惯,属于一个合格程序员的自我修养,于己于人,百利而无一害。

对于开发中的规范和约束,第一个要说的就是命名。

这五年多的工作,和形形色色的人合作过,记得最多的时候,我曾同时期开发和维护五个项目,业余时间,也曾和各路英雄好汉互相合作、互相学习和共同进步,在这个过程中,最让我觉得头疼的就是一些命名的不规范。

不规范的样式有很多,各种奇怪的命名都有。我曾看到过这样一串命名,其中有两个功能,一个叫做专栏详情,一个叫做专栏留言,命名却是 “ZhuanLaiDetalActivity” 和 “ZhuanLaiLiuYanActivity”,看得我很懵。

这样的命名,就问你怕不怕!对于这样的命名都怕了,那真没见过世面,这个至少还能看出个大概,之前看到过一些汉语拼音的缩写,比如动检证命名为 djz,这个看起来才更懵了。

对于拼音命名,这里说一点不知道会不会被喷,遇到过一些朋友总是喜欢拼音命名,汉语拼音是中华民族推动汉文化的伟大创举,但是在编程的时候用拼音,真的觉得好 low。

即使再牛逼的技术作支撑,写出来的代码也像小学生的作品,这里没有看不起汉语拼音的意思,只是发表下内心的一些想法。

建议:大驼峰、小驼峰或者下划线命名都可以,如果没有一个统一的标准,可以参考《阿里巴巴 Java 开发手册》,对于刚入行的朋友,更应该从命名抓起,对于以后的成长有很大的帮助。

开发手册下载地址:https://pan.baidu.com/s/1mjZxvSW

再者要强调的就是注释。很多人也许觉得注释是越多越好,之前也在书上看到过提倡多加注释,我觉得不然,有些时候注释给我们增加了很多负担和误解。

上次 Review 的时候发现,一些同事 Copy 我的一些代码的时候,其实是想做另一个功能,只是想把一些代码拷贝过去然后大修改(我不喜欢重复造轮子,对于相似的一些功能,最好做的灵活一点,提高代码的可重用性和灵活性)。

其实可以重用的地方很少,搞不明白为啥不自己写那么几行代码,这都不是事,让我很懵逼的是他们把我的备注和作者也拷贝过去了,当我进入那个类的时候,发现作者是我,去 Git 查看历史提交,完全没我啥事,而且功能描述和此类完全不相关……

对于注释,还有一点要说的就是一些多余的注释,这个叫需要和命名相结合,好的命名规范,可以省略好多不必要注释,比如 login、register,再加上登录、注册的注释,完全没必要。

良好的习惯,可以给我们开发带来很多便捷,但有些喜欢 textview1、textview2 命名的,这些就算加了注释,等下文用到的时候,看了也是一群羊驼在奔跑,上下奔腾的那种。
   for (int i = 0; i < j; i++) {
        // TODO
    }

对于这样的代码,可能很多人会觉得很正常,也会有部分人会把责任归咎于谭浩强老师,是的,谭老的书中问题确实很多,但这不是写这种代码的理由。

日常开发中,还有平时维护别人代码的同时,总会去调试 for 语句,难道不觉得这样的代码很糟糕,看得有点懵吗?就算加了注释,还是一坨一坨的。

因为每个团队有自己的规范和约束,大的公司,会有一套自己的规范,统一于各个团队,不同的语言也有不同的约束,如果日后有时间,会专门写一篇详细的约束与规范的 Blog 赠送。

对于这块,想写的东西真的好多好多,比如 case 后面的乱用,1、2、3 总是让人费解,比如必要的常量替代变量,比如线程池取代线程,比如必要的地方使用单例,东西真的好多好多,不再比如下去了,今天就先说明两条重点,命名和注释。

建议:合理使用注释,对于新手在学习期间,在陌生的代码和不清晰的逻辑上,尽可能多一点注释,便于理解,对于老鸟,尽可能规范的命名。

通过命名达到注释的效果,但是对于逻辑复杂或者操作状态太多的时候,必要的注释还是很重要的,减小维护成本。

一些应该熟知的编程思想

一个程序员用在写程序上的时间大概占他的工作时间的 10-20%,大部分的程序员每天大约能写出 10-12 行的能进入最终的产品的代码——不管他的技术水平有多高。

好的程序员花去 90% 的时间在思考、研究和实验,来找出最优方案。差的程序员花去 90% 的时间在调试问题程序、盲目的修改程序,期望某种写法能可行。

对于一个优秀的程序员来说,逻辑才是最重要的,他们愿意花更多的时间做思考,这样做的同时,就是更少 Bug 会出现,甚至可以把 Bug 率降到很低。

我并不是很优秀的开发者,但这些年依然有这么一个习惯,对于复杂或者多样的功能,总会先理清思路,先列举出会有哪些操作,哪些地方是 Bug 的雷区应该多注意,我也经常会和队友提起,一图胜千言,理清思路再下手,事半功倍。

不管业务逻辑是否复杂,上去就是干,发现有何不妥的再去修改,发现漏掉的再去添加,这样导致代码总是一坨一坨的堆在那里,经过多次的修改,已经面目全非,对于维护的人来说,更是苦不堪言。

在此,笔者也建议读者朋友,不妨试一试先绘图在动手,把一个模块继续拆解成一个个接口,通过实现接口去实现这个模块,做到面向接口编程,这样可维护性会提升好多……

对于模块与模块之间的通信,不应该是类与类之间的关联,而是通过抽象去实现交互,抽象不应该依赖于细节,细节应该依赖于抽象,这话比较绕口,说白了,就是面向接口编程,而不是面向实现编程。

这样做的好处就是,将来你要把这个被调用的类换成一个别的实现类时,你就不用去把调用过它的类一个个改掉了,因为它们调的是接口,接口没变,在配置里把接口的实现类换成新的类,就全部都替换掉了。

类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类引起波及。

建议:理清思路再下手,事半功倍。开发过程中,不妨先定义好接口,通过实现接口去完成模块的开发,尽可能的减小 Bug 率,写出更加优质的代码。

技术能力的提高,从代码上的体现主要在于 “高内聚、低耦合”,因为这些思想衍生出许多开发模式,比如现在比较流行的 MVC、MVP、MVVM 等。

版本迭代与重构

我们在做任何系统的时候,都不要指望系统一开始时需求确定,就再也不会变化,这是不现实也不科学的想法,而既然需求是一定会变化的,那么如何在面对需求的变化时,设计软件的可以相对容易修改,不至于说,新需求一来,就要把整个程序推倒重来。

相信很多朋友都遇到过,原本一个很普通的需求,在经历过 N 次迭代和修改后,已经形成一个庞大的功能,随着版本的不断迭代,维护起来的成本也随着越来越大,这样就形成了一种恶性循环,重构代码即将登上历史舞台。

不可否认,从维护成本上看,重构确实是一个很不错的方案,重构的成本比原基础维护的成本更小,也更方便以后的维护。有些公司甚至在多次版本迭代后,直接把整个项目推到重构,这样的事情不仅仅发生在小公司,在一些大公司,也是会发生多次。

从技术上来说,重构复杂代码时要做三件事:理解旧代码、分解旧代码、构建新代码。

而待重构的旧代码往往难以理解,尤其是在多次迭代且多人经手的模块;模块之间过度耦合导致牵一发而动全身,不易控制影响范围;旧代码不易测试导致无法保证新代码的正确性,尤其是在产品文档不全的时候。

这是上次 Review 时候发现的一段代码,先不说常量使用的不规范,这是经过一次次产品迭代后的结果,但这不是借口,造了这么多次轮子,真不应该。做为代码可重用性的反面教材,此处体现的淋漓尽致,如果有更多的状态,此处必然还是会重复多次……

建议:重构,并不是万能的,重构后的代码,当再次经历后续几个版本修改后,代码又显的杂乱无章,那一坨坨代码总是在不断的重演。

既然无法确定需求日后是否会修改,那我们只能通过提高代码质量来应对,以不变应万变,合理设计接口,每次更改需求时多思考,对于多次使用的代码进行封装提取,尽可能少的改动既有的逻辑。

设计模式的重要性

会建筑设计的是建筑工程师,不会建筑设计的是搬砖的。

前面已经说了很多,现在直接说一下设计模式的重要性,提到设计模式,就必须要提到六大基本原则和架构设计,提到架构设计,设计模式的重要性便可想而知。

首先,六大基本原则还是有点争议的,我之前看到的书籍中,一般都是单一职责原则、迪米特法则、里氏替换原则、开闭原则、依赖倒置原则和接口隔离原则。

但我最近在一些帖子上看到,有一种说法是没有接口隔离原则,而是合成 / 聚合复用原则,为了不影响之前的准备,合成 / 聚合复用原则会单独拿出来说一下。

六大基本原则,它是整个架构设计的灵魂,是架构设计的一种指导思想,而设计模式是架构设计的一种具体设计技巧,是架构设计的具体实践。

先从架构设计说起,对于架构设计,主要体现在抽象能力,抽象能力又依赖于架构者编码的阅历、功能的拆解和理解、逻辑的严密性。

做架构设计应该尽可能且更全面的考虑问题,尽可能做好代码的包容性,海纳百川,有容乃大之势,这是架构设计者应该具备的基本条件。

考虑的问题越周全,包容性越强,则工作难度越大,给自己造成的障碍也越多。合理的将这些细节问题进行抽象,并提出解决方案,抽象程度越高,解决方案越合理,这才是架构者的价值所在。

从具体的需求,到代码实现,再到具体的产品。架构设计的目的无外乎系统的复用性、扩展性与稳定性,具体的东西是无法很好地体现这些特性的,只有抽象的事物才能最好的体现。

在架构设计的过程中,单一职责原则告诉我们应该更好的体现高内聚、低耦合,这个类是用来数据请求的,就别放一些解析 JSON 的方法,如果这个类是用来图片加载的,View 的注解请隔离开,做到一个类只负责一个职责,只有一个引起变化的原因。

如果一个类承担的职责越多,就等于把这些职责耦合到一起,会带来一些不必要的维护成本。从大的角度来说,MVP 和 MVC 模式都是单一职责原则的体现,model-视图-控制相隔离,各司其职。

迪米特法则指导我们如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法时,可以通过第三者转发这个调用。

类之间的耦合越弱,就越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。主要是强调了类之间的松耦合。

对于里氏替换原则,或许很多人没听过这个名词,但是在实际开发过程中却无时无刻不在使用,其实很简单,子类型必须能够替换掉它们的父类型。

举个简单的例子,“List< String> list = new ArrayList<>();”,这么做的好处其实很简单,比如有一天 ArrayList 满足不了需求,需要改用 LinkedList,只需要把 ArrayList 替换成 LinkedList,而不是把全局的 list 对象都改一遍,提高了可维护性。

开闭原则是面向对象原则的核心,有两部分组成,对扩展开放,对修改关闭。软件需求总是会变化的,对软件设计人员来说,必须在不需要对原有系统进行修改的情况下,实现灵活的系统扩展。

对扩展开放,就是对抽象编程,而不是具体编程,因为抽象相对稳定,通过接口或者抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的 public 方法。

让类依赖于固定的抽象,所以,对修改是关闭的。这是建立在继承和多态的基础上,可以实现对抽象类的继承,通过覆盖其方法来扩展方法。

依赖倒置原则指的是依赖于抽象而不是依赖于具体实现,这一块在上述已经说过,其实就是面向接口编程而不是面向实现编程,这样做的好处就是解决耦合。

一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。

接口隔离原则认为,“使用多个专门的接口总比使用单一的接口要好”

一个模块应该依赖它需要的接口,需要什么接口就提供什么接口,把不需要的接口剔除掉,同时也应该遵循单一职责原则,这样避免臃肿的接口带来的污染,将没有关系的接口合并在一起,形成臃肿的大接口,就是对接口的一种污染。

接口的粒度也不能太小,太小会导致接口额数量剧增,对开发人员不友好;接口额粒度太大,灵活性降低,无法提供定制服务,给整体项目带来无法预估的风险,合理的设计接口,也是一门艺术。

对于这张图,一定存在很多的争议,因为很多设计模式都用到了多个基本原则,上图只是对设计模式的一个比较粗糙的总结。

强调六大基本原则 (含有合成 / 聚合复用原则) 在设计模式中的具体体现,同时也说明了六大基本原则和 23 种设计模式是相辅相成的,六大基本原则作为设计模式的基石和模板,设计模式是六大基本原则运用的灵活体现。

合成 / 聚合复用原则这个是存在一定争议的。目前有的书中还是保留了合成 / 聚合复用原则去掉了接口隔离原则,合成 / 聚合复用原则指的是少用继承,多用合成关系来实现。

合成和聚合都是对象建模关联关系的一种,聚合表示一种弱的拥有关系,整体由部分组成,部分可以脱离整体作为一个独立的个体存在,合成则是一种强的拥有关系,体现了严格的部分和整体的关系,部分和整体的生命周期一致,部分不能脱离整体。

总结

六大基本原则是面向对象思想的体现,单一职责原则与接口隔离原则体现了封装的思想,开闭原则体现了对象的封装与多态,而里氏替换原则是对对象继承的规范。

至于依赖倒置原则,则是多态与抽象思想的体现。在充分理解面向对象的基础上,掌握基本的设计原则,并且能够在项目设计中灵活运用,就能够改善我们的代码质量和结构设计。

尤其能够保证可重用性、可维护性、可扩展性和灵活性,这也是理解和掌握设计模式必备的知识。

补充

对于六大基本原则,这是我们开发都应该熟记于心并且灵活运用的,对于设计模式在日常开发中的运用,有一点还是要强调的,适合自己的才是最好的。

如果此时一个模块是很轻量级,仅仅为了使用设计模式而用设计模式,这无疑也会显得不伦不类,使项目变的臃肿,同时也带来一些不必要的维护成本(虽然维护成本很低)。

最近开始整理资料,准备写设计模式专题,主要是 MVP 爬坑与迪米特法则、Framework 与开闭原则、单例与爬坑、换肤与观察者模式、加载列表与模板方法模式、构造函数与建造者的对比、多个第三方登录与命令模式,后续还会继续完善,敬请期待。
  • 大小: 656.1 KB
  • 大小: 148.5 KB
  • 大小: 351.1 KB
  • 大小: 161.8 KB
  • 大小: 228.1 KB
  • 大小: 256.9 KB
来自: CSDN资讯
4
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 走近设计模式:写代码一定要用设计模式吗?

    摘要:不少人对设计模式都有些疑问或者说是质疑:写代码一定要用设计模式吗?用了设计模式的代码就比没用的好吗? 本文分享自华为云社区《走近设计模式:写代码一定要用设计模式吗?》,原文作者:技术火炬手。 ...

  • 我见过的最糟糕代码

    在本文中,我将向你展示我见过的一些最糟糕的代码,它们被称为“魔鬼代码”,会带来很严重的后果。然而,我们发现通过一些好的实践,你可以很容易规避它们。 “魔鬼代码” 需要改进的代码与所谓的“魔鬼代码”是...

  • 你们会因为代码烂,而入职两三天选择离职吗?

    其实早先有过一些关于代码质量的讨论,比如「关于烂代码的那些事」,「程序员的日常:哪个蠢蛋写的烂代码?」,「你的代码写的很烂」。这让很多程序员感受到共鸣,大家纷纷出来吐槽。大家都在抱怨同事的代码写的烂,...

  • 代码整洁之道——如何写出整洁高效的代码

    适合初学者和对代码规范一知半解的朋友

  • 代码该怎么写——设计原则

    初学者学会编程语言后开始写代码,当我们实现一个功能后会有成就感,但是看了别人写的代码又大感困惑,他为什么把代码写得那么复杂?明明一个简单的功能,为什么要这样做? 还有人即使学会了编程语言,仍然不知道...

  • 如何写出优质简洁的代码,这6个技巧要记得

    写干净代码的好处 先来了解编写干净代码的一些好处。其中一个主要好处是,干净的代码可以减少花在阅读上的时间和理解代码的时间。凌乱的代码会减慢任何开发人员的速度,使开发者的工作变得更加困难。​ 代码越...

  • 程序员,为什么千万不要重写代码?

    程序员都有一颗工程师的心,所以当他们到一片新的场地想做的第一件事就是,将旧的...你之所以认为旧代码一团糟,其实是由编程的一个基本定律决定的,那就是:写代码容易,读代码难。 为什么你觉得旧代码异常混乱?...

  • 程序员:这10种糟糕的程序命名,你遇到过几个?

    这10个极其糟糕的程序命名,你遇到几个? 有人问:规范的命名风格真的能让你程序员少出bug? 当遇到这方面的教训时,...不要觉得中文命名不可思议,我以前也是这样觉得居然还有中文命名的,上一家公司就有这样的例子。

  • 你真的会用 PyCharm 吗?推荐 14 个亲测好用的 PyCharm 插件

    “ 阅读本文大概需要 3 分钟。 ”写 Python,很多朋友都用的 PyCharm,包括我在内。但其实大部分情况下我们用到的功能可能仅仅占 PyCharm 功能的一小半都不到。本文推荐...

  • 写代码犹如写文章: “大师级程序员把系统当故事来讲,而不是当做程序来写” | 如何架构设计复杂业务系统? 如何写复杂业务代码?

    写代码犹如写文章: “大师级程序员把系统当故事来讲,而不是当做程序来写” | 如何架构设计复杂业务系统? 如何写复杂业务代码? Kotlin 开发者社区 “大师级程序员把系统当故事来讲,而不是当做程序来写” ...

  • 千万不要相信程序员在加班时间写的代码!

    其中最重要的就是这条:不要相信一个程序员在加班时间写出来的代码。 (软件工程的学说表明,连正常时间好好写的代码,也不要太相信。不过这不是本文的重点,略过不提。) (不懂代码的人,看到本文中的Java代码可以...

  • 真正的程序员到底应该是什么样子的?

    第一章(推荐看完整篇文章,再回过头看一遍第一章)我非常幸运出生在一个电脑和电子游戏还没有普遍的时代。所以我可以和我的小伙伴们一起玩耍,同时发明属于我们的游戏。我们十分会玩:用树枝做成'��'。我们可以用...

  • 单元测试,只是测试吗?

    首先我就来回答一下标题提出的问题:单元测试除了是一种测试手段外,更是一种改善代码设计的工具,容易写单测的代码往往也具有更加良好的设计。因而是任何自动化测试工具都无法取代的。当然,这里也不是把自动化测试...

  • 什么是真正的程序员

    这篇文章的原文来自:A Little Printf Story...所以我可以和我的小伙伴们一起玩耍,同时发明属于我们的游戏。 我们十分会玩:用树枝做成’????️’。我们可以用树枝做出任何东西,除’回旋镖’。因为你把树枝扔出去,

  • 想学python编程-想学Python编程?你真的适合吗?

    你真的适合吗?有的人说我想学什么、我想干什么,很多时候都是头脑发热,单凭一腔热血,可是这样的路即便走上去你又能坚持多久呢?所以,每每有人问我学Python编程怎么样,我都会反问一句:你适合吗?是看着Python编程...

  • 代码审查“查”什么?

    让我们来谈谈代码审查(Code Review)。如果花几秒钟去搜索有关内容,你会发现许多论述代码审查好处的文章(例如,Jeff Atwood的这篇文章)。你还会发现许多介绍如何使用代码审查工具的文档,比如我们常用的Upsource...

  • 【翻译】我为何爱读代码?你为何也应当爱?

    原帖地址:http://www.luanxiang.org/blog/archives/1092.html作者:...可是,读代码真是不容易,而且还很烦人,又无可逃避,其他人写的代码总是很垃圾(即便不说,我们心里也这么想的)。即使自己写的代码,过几个小时

  • 重写代码,是否还要继续?

    程序员都有一颗工程师的心,所以当他们到一片新的场地想做的第一件事就是,将旧的一切推倒重来。...你之所以认为旧代码一团糟,其实是由编程的一个基本定律决定的,那就是:写代码容易,读代码难。 为什么你觉

  • 基于springboot大学生就业信息管理系统源码数据库文档.zip

    基于springboot大学生就业信息管理系统源码数据库文档.zip

Global site tag (gtag.js) - Google Analytics