摘要:探究事物的反面性是一种哲学上的思考,物理学在“物质”之上提出并证实了“反物质”的存在,就像数学上有正数也有负数,武侠小说中有九阳神功也有九阴真经,生活中有婚姻也有出轨,事物总是充满这种相互矛盾而统一的有趣现象。那么放在GoF提出的23种提倡使用的设计模式上,是否也存在反设计模式,或者反模式的存在呢?答案是显而易见的。而且从辩证的角度来看,一个设计模式在特定的场合下是积极并且优势显著的,但是在偏离最佳适合场景时,它本身就转变为一个反模式,从而导致不良影响,就像现实世界中没有所谓纯粹的好人或者坏人一样。托翁说:“幸福的家庭都是相同的,不幸的家庭各有各的不幸”,放在这里,则是“设计模式都是相同的,反模式则各有各的问题”。

1.反模式的历史

根据维基百科的定义,设计模式是一套被反复使用,多数人知晓的,经过分类编目的代码设计经验的总结,是软件设计的某些特定场合的某些问题的解决思路。是前人经过大量的实践,总结出来的无论从效率、扩展性、复用性、可靠性等方面都显现出优势的解决思路。反模式(anti-pattern)指的则是在实践中明显出现,但低效或有待优化的设计模式,是用来解决问题的带有共同性的不良方法。反模式经过研究分类,能在研发系统尚未投产时被辨认出来,防止日后重蹈覆辙。

Andrew Koenig在1995年首创了anti-pattern这个词,灵感恰恰就来自于GoF的著作《设计模式》,是这本书首先在软件领域引入“设计模式”的概念。三年后anti-pattern因《antipatterns》这本书获得普及,它的使用也从软件设计领域逐渐扩展到了日常的社会互动中。按《AntiPatterns》作者的说法,区分反面模式和不良习惯、错误的实践或糟糕的想法可依据至少两个关键因素:

其一,行动、过程和结构中一些重复出现的,乍一看有益但最终得不偿失的模式;其二,在实践中证明且可重复的有清晰记录的重构方案。

很多反模式只相当于是错误、咆哮、不可解的问题,或是有可能避免的糟糕实践,它们的名字通常是一些用反话构成的词语。有些时候陷阱(pitfalls)或黑色模式(dark patterns)这些不正式的说法会被用来指代各类反复出现的糟糕的解决方法。

反模式其实体现的是一种积极反思的行为。通过对不断出现、糟糕透顶的解决方案反思之后的深刻总结,让我们能够从错误或者失败中学习提高,避免出现相同或类似的问题,提升效率。理解了反模式,有助于我们在实际工作中预防并改正它们。

有一篇文章,讲到用5种设计模式来完成一个Hello Word的打印,使用关键字“write Hello Word with design patterns”谷歌一下就能找到(请使用正确的姿势访问谷歌),甚是有趣。然而有趣背后的反思,却是“为了设计模式而设计模式”,成了一种典型的反模式“为了XX而XX”。

从图书网站上,也可以搜索到一些反模式的书籍,比如《测试反模式》、《SQL反模式》、《Python反模式》等,连最近很火的微服务也有了反模式,这些书中都总结了一些最差实践,供我们学习并避免。

2.反模式中的神仙大类(倚天剑)和黄金大锤(屠龙刀)

反模式有很多种,我这里想给大家介绍其中的两种,分别是神仙大类和黄金大锤,我这里把他们比喻成倚天剑和屠龙刀,在于这种大杀器一般要谨慎使用。因其威力太大,用得不好的话,就极可能伤及自身。或者没有伤到自身,伤到边上无辜的花花草草也是不好的。

神仙大类,又称God Class, Blob Class等,是一类拥有太多属性和方法(比如超过20个)的模式,它能处理的事务涉及方方面面(比如员工类,涉及工资计算、税务计算、入职离职、数据库读写、请假报销等等),它所占用的代码行数从数百到上万行。“神仙大类”是KISS原则和SRP原则的反模式。我自己虽然未曾有幸见识过上万行的大类(倒是听说过不少),但确实见过超过2000行的只有一个main函数的大类。针对这种大类,我是该献上我的膝盖还是我的口水呢?我想刚开始是膝盖,因为不明觉历,后来铁定就是口水了,因为我不幸接手并负责维护它,每次只要一改它,在上线的时候我都会心惊胆战,晚上睡不好觉。

当然,对于一锤子买卖,如不再需要维护的代码,或者需求绝对不会变更的代码,这种神仙大类就让它逍遥去吧。唯一的不足之处就是,不能为下一个项目提供可复用的单元,只能看着年龄在增长,技能和效率却没有什么提升。普通程序员只是年复一年地完成日常的业务需求,没什么代码复用可言。好的程序员却可以在完成日常业务开发的同时,不断地总结并丰富自己的代码工具箱,代码复用率很高,真正需要写的业务代码也写得非常少,有时只需要做一下配置,就可以完成类似的事情。

神仙大类的拥趸者说,你看,我一个大功能一个大类就搞定了,一个大类就一个文件,如果按照你的那套所谓的SRP/KISS,至少要不下20个类的小文件,我不也算符合简单化原则了,一个文件还不算简单嘛。乍一听,我竟无言以对,如果你一个类文件里面也是分了各种层次,做了各种不同抽象设计的话,好像不无道理。只是,这种情况下,一个文件里面那么多功能,如果想重用其中的一个,咋办呢?是不是得把整个大类照单全收,还是把要重用的那个小函数拷贝一份出来?只是同一段代码一旦重复拷贝,就违反了DRY(Don’t Repeat Yourself )干燥不渗水原则,而被 WET(Write Everything Twice)湿漉漉反模式给狠狠地砸脸了。

神仙大类,本质上就是一个“集大成”的大胖子,在这个以瘦为美以减肥为时尚的今天,确实不受欢迎。你虽然不能像林丹那样拥有8块完美腹肌,但是你可以让你的代码做到啊,只需要远离神仙大类,或者使用“人挡杀人佛挡杀佛”的重构“拆”字诀把遇到的神仙大类就地拆成大约8个各司其职的小类就可以了。

黄金大锤,Golden Hammer,指使用相同的工具、产品或技术,解决几乎所有的问题。如果你只有一个关系型数据库,那么任何问题都看上去是其中的一张关系表。或者学习了设计模式后就开始肆无忌惮地到处用设计模式,就连最简单的打印一个Hello Word的入门程序也都能用上几个设计模式的话,那就是把设计模式当成黄金大锤了。

有一种“面向接口编程”滥用的反模式,就是“一个服务一个接口”。这种常见的就是所有的服务类都有一个所谓的XxxService及XxxServiceImpl,前者是一个接口,后者是对应的惟一实现。问题的关键在于,这种XxxService和XxxServiceImpl竟然一一对应,也就是说一个XxxService其实只有一个XxxServiceImpl与其对应。这如果不是对“面向接口编程”的一种曲解与滥用,那就是“夸夸其谈的未来性(Speculative Generality)”的代码坏味。“接口”在面向对象的设计中,是属于抽象层面的东西,那什么时候需要抽象呢,一定是两种及以上事物拥有一些共同特征时,才能形成抽象(自底向上);或者从高层定义一些抽象特征,由两种或以上事物来体现这个特征,这种抽象才有意义(自顶向下)。比如光喊我一个人吃饭,你根本不需要抽象,喊我的名字我就来了(我的思路是,吃饭不积极肯定有问题),但是我跟很多男的在一起的时候,你喊:“IT男们,走啰”,我们就一起过来了。那么这种“IT男们”就是一种抽象,只当有两个以上的实体的时候,这种抽象才有必要和更有意义。

在数据类型使用方面往往也有类似的锤子问题。比如涉及到List的统统都是ArrayList,涉及到Map那就都是HashMap了,其它类型那就统统String,好像其它的都不存在了一样,这就相当于把String、ArrayList和HashMap当成了处理全部数据类型的黄金大锤了。

我曾见识过一个根据电话号码段查找归属地的实现,堪称经典。原始实现是,将数据库包含起始号码、结束号码以及归属地市几个字段的表中所有记录,按起始号码排序后一次性地读入到程序内存中,然后每次查找特定号码的归属地时,在数据结构中顺序查找比对,使用的数据结构是ArrayList<HashMap<String, String>>的模式。这个在数据量较小时,好像不是什么问题,因此也在线上欢快地运行了若干年。直到表记录达到十万多条,经常出现程序加载缓慢导致发布经常性失败,或者查找归属地十分缓慢的性能问题时,深藏了那么多年的实现问题才浮出水面。有一个反模式叫做“过早优化”,还有一个兄弟反模式叫做“过晚优化”。这个案例,不只是“黄金大锤”的反模式,还是“过晚优化”的反模式。这个实现我后来review了一下,首先是十多万条记录有太多零星的单个号码成一个号段的记录,实际上可以归并到三万多条左右记录;其次是电话号码不要使用String,转用Long来表示;再次是可以使用TreeMap来完成快速定位查找而不是List顺序查找,具体实现可以参考https://gist.github.com/bingoohuang/5916691 。

在阅读代码时,还经常会看到这种函数的入参和返回的类型都是Map,OMG感觉就是一个黑洞,没有任何语义,只知道是一个大麻袋,里面是什么,深不可测。比如下面这个activeVcher函数,短短几行代码中里面就出现了4处Map,我想写这个代码的人一定是丐帮中的四袋弟子。当然这也怪我当初刚入道JAVA时留下的一个“债务”,没想到6年以后,债务依然还在向前滚动。当年我还不知道代码可读性,更不知道POJO的意义,以及语义化的涵义,只是感觉Map好灵活好喜欢。只是今日回头一看,竟然有种在浓浓的雾霾中看不清前路的感觉,看到Map摸不清里面到底兜了些什么,套句流行的话说“雾是String的浓,霾是Map的厚”。

3.总结

入了设计模式的门,又摸了一下反模式的皮毛,到此我们终于大概摸清了模式这头大象比较完整的轮廓了,大家可以尝试骑象远行了。在日常工作中,我们也可以尝试把一些常见的写法进行归纳总结,好的命名成XXX模式,不好的就命名成YYY反模式。只要坚持不懈,最终我们可以超越模式本身,进一步提升认识,跨越到“码可码、非常码,道可道、非常道”的境界之中了。

设计模式杂谈——模式与反模式之争相关推荐

  1. 反模式设计_设计模式:模式或反模式,这就是问题

    反模式设计 我最近遇到了Wiki页面" Anti-pattern" ,其中包含详尽的反模式列表. 其中一些对我来说很明显. 他们中的一些让我想了一下,其他的让我想了更多. 然后,我 ...

  2. 设计模式:模式或反模式,这就是问题

    我最近遇到了Wiki页面" Anti-pattern" ,其中包含详尽的反模式列表. 其中一些对我来说很明显. 他们中的一些让我想了一下,其他的让我想了更多. 然后,我开始在页面上 ...

  3. SOA系列文章(二):服务设计原理:服务模式和反模式

    服务设计系列的法则已经发展到最佳通信实践和取样相关编码的程度.本文提供了设计和实现网络服务的基本原理,并且对面向服务的体系结构(SOA)的相关概念做了一个简要的回顾,以及有关于几种模式和反模式的详细讨 ...

  4. 复制模式和扩展模式_扩展剂:模式还是反模式?

    复制模式和扩展模式 扩展器模式在最近几年变得很流行,甚至已经在OSGi标准(例如,蓝图服务和Web应用程序规范)中使用. 在处女座,我们从一开始就与扩展程序一起工作,但是尽管它们具有优势,但它们仍有一 ...

  5. 扩展剂:模式还是反模式?

    扩展器模式在最近几年变得很流行,甚至已经在OSGi标准(例如,蓝图服务和Web应用程序规范)中使用. 在处女座,我们从一开始就与扩展程序一起工作,但是尽管它们具有优势,但它们仍有一些明显的缺点. 由于 ...

  6. DevOps 模式与反模式索引

    我今天把 DevOps 模式和反模式做了一个简单的总结.如果全职写,半年可以写完.如果周更,需要两年,我怕自己烂尾,夜长梦多. 自己开的坑,含着泪也要把它填完. DevOps 策略模式 模式:定义你的 ...

  7. 测试驱动开发系列之五--测试的模式与反模式

    14.1喋喋不休的测试反模式 刚刚接触TDD或者刚开始写单元测试的人倾向于重复一些相同的错误.这些常见的但是会降低生产力的模式称作反模式"antipattern".可以通过应用四阶 ...

  8. 规避软件架构风险之反模式

    在QCON大会上,Michael Nygard,以及 李伟专家都提到了一个概念,容错能力. 衡量软件架构最佳的一个很重要的因素就是看软件的容错能力.没有容错能力的软件,哪怕你QA都非常优秀,但一发生故 ...

  9. [译] How to NOT React:React 中常见的反模式与陷阱

    原文地址:How to NOT React: Common Anti-Patterns and Gotchas in React 原文作者:NeONBRAND 译文出自:掘金翻译计划 本文永久链接:g ...

最新文章

  1. Redhat7 samba配置(部分)
  2. 项目中cxf和weblogic整合时报错的问题
  3. mysql 默认sql mode_MySQL的sql_mode模式说明及设置
  4. C语言,利用一维数组交换法排序,使学生成绩高低排序(要求输入为负值时输入结束)
  5. 牛x!一款比传统数据库快 100-1000 倍的数据库,来认识一下?
  6. face 3000 C++ 代码理解
  7. 泛型与容器连载(一)泛型的基本概念和原理
  8. Netty in action—Netty传输服务
  9. 创建mysql数据库远程工具连接及ER图工具mysqlWorkbench使用
  10. 一种定位激光在摄像头画面中位置的方法(附安卓源码)
  11. 【安卓项目】期末大作业——“记账宝”APP开发案例
  12. 为基因序列片段在NCBI的GenBank数据库申请登录号
  13. 计算机组成原理中总线包括,计算机组成原理复习题
  14. Spring事务管理A方法内部调用B方法的回滚问题(springboot事务管理)
  15. 基于云端的生命检测系统(合泰杯比赛报告内含代码)
  16. speedoffice表格如何快速复制整张工作表
  17. linux 用户 组区别吗,Linux用户组之主组和附加组
  18. HTML5特效(shadow、gradient、transition、transform、filter)
  19. 欧式距离与曼哈顿距离的区别以及曼哈顿距离的应用
  20. 特殊符号: 和 | | 和 ?? 和 ?作用详解

热门文章

  1. Java基本类型介绍
  2. MFC实战篇——分页功能
  3. web入侵二之弱口令攻击及其他
  4. 本地电脑与远程桌面无法复制粘贴怎么办
  5. 万维网之父:蒂姆·伯纳斯·李
  6. top期刊,顶级期刊,顶级期刊,jcr分区和中科院分区
  7. 备忘1:爬取热门微博评论
  8. “盲盒抽奖”创意营销活动实践
  9. Linux文本编辑工具
  10. (jsp一)概述及服务器配置