文末有惊喜
介绍设计模式,尤其是在面试中回答一个设计模式时,如果有一个能够贴近设计模式本质的通用公式来介绍这些模式,不仅对使用者来说更容易记忆,对听者来说也很容易理解到设计模式的共通点,即“当我们讨论起某个事物时,就会想起什么什么”。
今天想起来写这篇笔记,也是因为最近面试被问到设计模式时,根据每次回答后的感受,隐隐感觉设计模式里面有些什么套路以前没有总结过。于是就想整理出一个通用的方法去应对设计模式这个知识点,这样以后无论是面试还是学习其他设计模式都有迹可循,不再杂乱无章。
什么是设计模式?
在java中它是java类和对象组成的某种形式——from《java与模式》1.5节 软件的永恒之道“三论门”。
描述设计模式的通用公式?
通用公式:XXX设计模式中可以抽象出XX种角色,分别是X, XX, XXX。每一种角色的功能是XXX......XXX模式中将这几种角色组合起来的形式是XXXX。(有必要的话,再补个小结)
公式中最重要的(也即理解设计模式时最重要的)是什么?是“角色”。通过角色不同的功能点,也就对应了不同的设计模式。中国人一般能从小就学编程的不多,而且编程的语言多少带有点外语的思维,但是我们起码对自己的母语还是最熟悉的。将编程中遇到的问题,抽象为自己生活中熟悉的角色,对自己理解一个新东西通常可以起到加速的效果。
下面来看下这个公式的效果:
面试官:“听说你熟悉设计模式对吧,说一下桥接(桥梁)模式?”
我:“(开门见山)桥接模式或者叫桥梁模式,是OOD思想的良好实践,它指导我们在OOP(面向对象编程)时不要滥用继承关系,滥用继承堪称是俄罗斯套娃式编程。(开始套公式)桥接模式中可以抽象出三种角色,一种是抽象角色,一种是具体角色,还有一种是桥梁角色。抽象角色对应了抽象接口,具体角色对应了具体实现类。在桥接模式中,提倡不使用继承关系来耦合不同类的关系,而是通过桥梁角色使用聚合的方式来组织类对象的关系。具体到代码中就是我们可以在桥梁角色类中,拥有A、B、C等多个不同接口的域,并且给这些域指定各自的实现类。(讲一点这个模式的小结)当我们拥有不同的桥梁角色类时,就可以拥有A、B、C这三类接口的不同实现方案,并且还能保持A、B、C这三个接口各自独立的变化”。
上面示例中的“开门见山”,我是后补的,通过套用公式先写出主体,然后看看还有没有可完善的,我觉得就算没有“开门见山”那一小段,这个回答也算比较通顺了。
经过本次使用公式后,我完全感觉到这个公式的好处,条理变得清晰了。再看下下面这个例子:
面试官:“你再说说代理模式?”
我:“代理模式里通常有三种角色,第一种角色,真实角色:实际执行某个具体方法的角色;第二种角色,代理角色:代理角色内部包含真实角色对象的引用,它可以控制真实角色对象的创建、调用。并且代理角色中的调用并不是简单地对真实角色对象的调用,通常还要在调用前后做点什么;第三种角色,抽象角色:这个角色声明了真实角色和代理角色共同的接口,这样一来任何使用真实角色的地方都可以使用代理角色替换并且不会出现问题。举例来说,在JDK动态代理中,会要求具体类(真实角色)要实现接口,这个接口就对应了前面说的“抽象角色”。”
代理模式直接套用公式来回答,最后的“举例”算是一个小结
公式真的万能吗?试试套用到创建型的工厂模式三兄弟上效果如何。
——简单工厂模式:(套公式)简单工厂模式涉及三个角色:工厂类角色,抽象产品角色,具体产品角色。(介绍角色)工厂类角色是简单工厂模式的核心,通常该类包含必要的判断逻辑,可以决定什么时候创建哪一个产品类的实例,且该工厂中的方法是静态方法,所以也有人叫它静态工厂。抽象产品角色是工厂类角色创建的对象的父接口,并且静态方法返回的类型也是抽象产品角色类(DIP)。具体产品角色是由工厂类角色创建的各个抽象产品角色的具体子类。(简单工厂模式小结)简单工厂模式的优点是将创建对象的细节从客户端转到工厂类中;缺点是工厂类成为了上帝类,所有创建逻辑都在它内部,拓展时不方便。
——工厂方法模式:(套公式)工厂方法模式涉及四个角色:抽象工厂角色,具体工厂角色,抽象产品角色,具体产品角色。(介绍角色)工厂方法模式是简单工厂模式的升级,它将简单工厂中的工厂类角色的继承等级提升,分为抽象工厂和具体工厂两种角色。(工厂方法模式小结)有了抽象工厂角色,就说明将会诞生不同的具体工厂角色。在代码上体现为客户端可能需要操作多个具体工厂来获得不同的具体产品。所以工厂方法模式使用场景比简单工厂模式更复杂一些。
——抽象工厂模式:抽象工厂模式涉及四个角色:(套公式)抽象工厂角色,具体工厂角色,抽象产品角色,具体产品角色。(介绍角色+小结)抽象工厂模式的角色和工厂方法模式是完全一样的,不同之处在于,抽象工厂模式重点在“抽象工厂”一词上,实际情况中就是在抽象工厂角色中可能会提供多个工厂方法,对应不同的产品族去使用。比如 OperationSystemAbstractFactory 抽象工厂角色中可以有 UnixFactory 和 WindowsFactory 两个工厂方法,两种工厂中都要提供对网络模块、IO模块、视窗模块的支持。最后对比抽象工厂模式与工厂方法模式,前者会多一些产品族,对应的项目规模更庞大。
可以看到在介绍这种有递进关系的设计模式时,公式仍然可以使用,但是介绍结构也会产生“有递进”的变化。
(悲伤的事,在写这段的时候,看了一篇朋友发来的博客,标题为《圣杯与银弹·没用的设计模式》,看完之后导致这篇文章对其他模式的小结直接夭折了,下面会具体提到这篇好文)
有了公式,你还怕什么呢?没错,就是“巧妇难为无米之炊”~:(
这个就有点尴尬了,有些模式自己平时根本不使用,而且也没背过,即使有公式也是“巧妇难为无米之炊”啊~~~
所以在准备面试时设计模式这块,如果有充足的时间,套用公式去把每一个模式记一遍,理清它们的角色和关系,我相信背起来应该不会太吃力。如果时间不充裕,就记几个常见的设计模式就可以了。
最后,推荐一篇好文,值得你花十五分钟好好看看
就是那篇导致我没有兴致再去写其他设计模式总结的好文,《圣杯与银弹·没用的设计模式》。
需要简单说明一下,标题中的“没用”,指的不是设计模式(或者应该说是软件设计技巧)本身没用,而是《设计模式》相关的书“没用”。
看完这篇文章,终于理解了为什么每次看《设计模式》一类书总是印象极其不深,看三遍这个和看三遍HashMap源码的收获完全不成正比,看源码好歹一次比一次理解得更多,也能更轻松记住更多源码。而看《设计模式》这类书完全是看过就会慢慢忘掉,而我现在又不是特别喜欢死记硬背的方式。还好今天看到这篇文章,通过《设计模式》的一些历史,和博主介绍的提升软件设计水平的方法,解决了我对于《设计模式》这类书以及如何提升软件设计水平的疑惑。
以下是我看完该博客从中提取和总结的心得:
1.设计模式真的是尚方宝剑吗?看看过程——1994年出版的书,2000年引入不温不火,2014年互联网后时代才逐渐爆发成为高频“面试词汇”。这个历史成绩并不理想。
2.设计模式是从许多项目中总结出来的通用解决方案,为了通用舍弃掉了非常多的细节,而魔鬼通常藏在细节里。从具体到抽象相对容易,从抽象到具体会变得非常难。所以项目经验相对不足的开发者会发现在具体项目中套用抽象的设计模式很难。
3.学习好设计的方式就是多读开源项目的源码。学习它们的顶层设计到底层实现。顶层设计可以给你带来架构思想,底层实现则可以提高你的实际编码能力。java开发者可以从jdk开始,到常用的第三方库。
最后的最后,引用该文作者最后一段话:
通过看书来学习软件设计几乎是一个不可能完成的任务,作者认为学习如何为程序编写单元测试对学习系统设计极其重要,提升项目单元测试覆盖率的过程会让我们思考如何写出更利于测试的代码,虽然软件工程中没有银弹,但是单元测试不是银弹可能也所差无几了。