写在前面:
学习过程中不仅要熟练掌握技能,理论的消化吸收也必不可少。虽然个人更倾向于学习技术类的东西(短时间的精力投入很快就能看到成效...),但看了很多前辈的经验总结后才知道理论性的东西是绝对不能忽视的,毕竟理论对实践有着重要的指导意义。而了解“设计”相关的东西,会对“实现”产生潜移默化的影响,虽然不能在短时间内看到让人欣喜的变化,但可能有一天回过头来一想“哦~,原来我这个不错的小习惯是当年在那本书上学到的啊”
一.什么是设计模式?
绕了一圈之后让我们又回到这个话题吧(看书之前问自己一遍,看完之后再问自己一遍,答案之间的差异就是感悟了)
之前:
自问:设计模式是什么?
自答:是一些系统设计的指导原则吧,不过我现在天天写代码,应该也用不到设计层面上的东西。
自问:哦,那具体是什么?
自答:不清楚,不过我见过一些前辈的代码,结构很复杂,一看就很上档次的那种,那些应该就用了设计模式吧,所以代码有没有应用设计模式应该就是高手与新手的区别了。等我成了高手之后,这种东西还不信手拈来,嗯,不说了,我写代码去了,好好积累项目经验。
...
之后:
自问:设计模式是什么?
自答:设计模式是由代码结构优化经验萃取出来的理论知识,应用成熟的设计模式能够增强代码的可复用性、可扩展性与可维护性。
自问:你好像很专业的样子,那好,既然设计模式有这么多好处,那是不是应用了设计模式的设计都是好设计?
自答:当然不是,我们不能为了使用模式而使用模式。设计模式可不能滥用,毕竟应用设计模式必须要作出一些牺牲(比如增加类结构的复杂性...),所以滥用设计模式的话是会出事的。而且,就算我们有了锤子,也不能把所有问题都看作钉子吧?
...
P.S.还记得学习正则表达式的总结:能不用就尽量不要用(最大限度的避免滥用),设计模式与之类似,不能看到什么问题都往模式上靠,只有当我们非常确定在当前情景下确实需要用设计模式来优化我们的设计时,才考虑使用设计模式(至于到底用不用,还要权衡重构的成本与优化的收益...)
二.要不要使用设计模式?
这是个值得思考的问题,毕竟现在我们已经拥有了一把锤子,要不要用它当然成了问题,毕竟不是所有的问题都可以用锤子来解决。退一步讲,即便所有问题都能用锤子解决,我们也不确定使用锤子是不是最好的解决方案(拔钉子的话,可能用钳子更好些...)
当我们拿着某个设计模式想放进我们的代码中时,最好权衡一下利弊,诚然,设计模式具有的设计上的弹性一定会给我们之后的维护变更带来些便利。但是利与弊到底哪个更多一些,我们需要先回答几个问题再做决定:
- 我们的项目是不是几乎不涉及维护或者没有后续版本,那么我们引入设计模式还有必要吗?
- 我们项目的规模是不是大到了不用设计模式不行的地步?
- 这个设计模式用在这里合适吗?有没有更合适的?
- 非要用设计模式吗?可不可以用几个简单的设计原则来代替?
- 引入设计模式之后,代码结构的复杂度大大增加,重构的成本我们可以接受吗?
如果深思熟虑之后,还是觉得使用设计模式比较好,那么,放心去用吧,之后好好享受设计模式带来的好处吧
三.设计原则总结
设计原则都是一些简单的指导意见,没有固定的实现,因而设计原则也更加灵活,常见的设计原则如下:
- 封装变化(把易于发生变化的部分抽出来,以减少其变化对其它部分的影响)
- 多用组合,少用继承(组合比继承更有弹性)
- 针对接口编程,不针对实现编程(使用接口可以避免直接依赖具体类)
- 为交互对象之间的松耦合设计而努力(更松的耦合意味着更多的弹性)
- 类应该对扩展开放,对修改关闭(open-close原则)
- 依赖抽象,不要依赖具体类(减少对具体类的直接依赖)
- 只和朋友交谈(密友原则)
- 别找我,我会找你(Don't call me, I will call you back.安卓开发的大原则)
- 类应该只有一个改变的理由(单一责任原则)
能用设计原则解决的问题就不要用设计模式(杀鸡焉用宰牛刀...),因为设计原则实现起来更加灵活,更加轻巧(不用去考虑模式的条条框框...)
四.设计模式总结
名称 | 特点 |
策略模式(Strategy) | 把可以替换的算法步骤封装成一个个算法族,供运行时动态选择 |
观察者模式(Observer) | 定义并维护对象之间的一对多关系 |
装饰者模式(Decorator) | 建立拥有共同超类的装饰者与被装饰者来实现功能的动态扩展 |
工厂模式(Factory) | 封装对象的创建过程,包括工厂方法模式和抽象工厂模式 |
单件(例)模式(Singleton) | 用来创建唯一的对象(比如数据库连接对象,线程池对象等等) |
命令模式(Command) | 封装方法调用细节,解耦请求者与执行者 |
适配器模式(Adapter) | 用来实现不同接口间的转换 |
外观模式(Facade) | 为复杂的子系统提供简单易用的高层接口 |
模版方法模式(Template Method) | 用来封装算法骨架(流程),某些步骤由子类实现 |
迭代器模式(Iterator) | 用来封装遍历细节 |
组合模式(Composite) | 提供一种层级结构,使得我们能够忽略对象与对象集合间的差异,一视同仁地对待它们 |
状态模式(State) | 把所有动作都封装在状态对象中,状态持有者将行为委托给当前状态对象 |
代理模式(Proxy) | 通过插入第三方(代理对象)来分离调用者和被调用者(不同于执行者) |
复合模式(Compound) | 将多个模式组合结合起来形成一个“框架”,以解决一般性问题 |
桥接模式(Bridge) | 将抽象的控制类与具体实现类通过组合解耦,使得抽象层类与实现层类可以对立与对方而变化 |
生成器模式(Builder) | 用来封装组合结构(树形结构)的构造过程,与迭代器模式类似,都隐藏了组合结构的内部实现,只提供一组用于创建组合结构的接口 |
责任链模式(Chain of Responsibility) | 让一个请求可以被一组接收者顺序处理,类似于Android处理请求的方式:一个接收者捕获请求后可以return true消费掉请求,也可以return false传递给接收者队列中的下一个接收者(观察者) |
蝇量模式(Flyweight) | 抽象出对象管理层来统一管理大量的同类型对象,以减少运行时对象实例的个数,减少内存消耗 |
解释器模式(Interpreter) | 用来为简单语言创建解释器,将语法规则直接映射为各个类,结构简单,但效率较低 |
中介者模式(Mediator) | 引入中介者来封装多个对象间的复杂交互,以降低同级(在类结构统一层次上的)对象间的依赖 |
备忘录模式(Memento) | 支持对象状态的保存与恢复,并将对象状态数据封装起来,独立于客户代码以提供保护(Java中可以结合序列化反序列化技术来实现该模式) |
原型模式(Prototype) | 以现有的对象为原型,通过clone得到新的对象(以简化新对象的创建过程) |
访问者模式(Visitor) | 为组合结构添加新的操作,而不需要频繁的改变组合结构 |
五.面向对象的设计(Object Oriented Design)
一直伴随着OOD的问题就是“折衷”(或者说是“取舍”),最简单的例子——要不要用设计模式?
- 用,意味着将产生复杂的类关系,多层的抽象,我们将牺牲易读性换取易扩展性、易维护性或者其它特性
- 不用,意味着我们不需要对现有代码进行重构(或者不用去在复杂的设计上耗费过多的时间),但使用设计模式的所有好处我们就都享受不到了
二者选其一,这就是一个“取舍”,或者创造第三个选项(比如使用设计原则),这就是一个“折衷”
写在后面:关于《Head First设计模式》
很少写书评,因为每个人的喜好不同,不好作推荐(甚至有时候喜欢一本书仅仅是因为喜欢作者的行文风格而已,从而不自觉的认为书籍内容不错。。)
这本书是前辈推荐的,看完之后觉得大部分章节讲的很不错,既细致又通俗,但感觉某些章节并不是很好(比如第一章策略模式的例子不很贴切,和后面的远程代理部分细节展开的不合理)
总体来说,作为入门书籍的话,《Head First设计模式》还是不错的,个人感受,仅供参考