设计模式Head First
https://book.douban.com/subject/2243615/
OO基础:
- 抽象
- 封装
- 多态
- 继承
OO原则:
- 封装变化
- 多用组合,少用继承
- 针对接口而非实现编程
- 为交互对象之间的松耦合设计而努力
- 对扩展开放,对修改关闭(OCP)
- 依赖抽象,不依赖具体类
- 只和朋友交谈/最少知识原则(减少暴露的接口,减少耦合)
- 别找我,我会找你(好莱坞原则)
模式是在某个情境(context)下,针对某问题的某种解决方案。
一个设计模式,应该由下面几个部分组成:
- 名称(Name): 如果没有好的名称,该模式就无法成为你和其他开发人员之间共享词汇的一部分
- 意图(Intent): 该模式的作用,或者是该模式的定义
- 动机(Motivation): 问题以及解决这个问题的具体场景
- 适用性(Applicability): 该模式可以被应用在什么场合
- 结构(Strucuture): 参与此模式的类之间的关系图示
- 参与者(Participants): 此设计中所涉及到的类和对象在模式中的责任和角色
- 协作(Collaborations): 告诉我们参与者如何在此模式中协作
- 结果(Consequence): 采用此模式之后可能产生的效果,好与不好的
- 实现和示例代码(Implementation/Sample Code): 在实现中的技巧以及可能遇到的问题
- 已知应用(Known Uses): 用于描述已经在真实系统中发现的模式例子
- 相关模式(Related Patterns): 和其他模式之间的关系
设计模式也存在缺点。设计模式通常产生一些额外的类和对象,所以会增加设计的复杂度。设计模式也会在你的设计中增加更多的层,这不但增加复杂性,而且效率下降。
共享模式词汇的威力
- 共享的模式词汇威力强大。当你使用模式名称和其他开发人员或团队沟通时,你们之间的交流不只是模式名称,而是一套模式背后所象征的质量,特性,约束。
- 模式能够让你用更少的词汇,做更充分的沟通。当你用模式描述的时候,其他开发人员便很容易的知道你对设计的想法。
- 将说话的方式保持模式层次,可以让你待在“设计圈子”久一点,使用模式谈论软件系统,可以让你保持在设计层次,不会被压低到对象与类这种琐碎的事情上面。
- 共享词汇可帮你的开发团队快速充电。对设计模式有深入了解的团队,彼此之间对设计的看法不容易产生误解。
- 共享词汇能帮助初级开发人员迅速成长。初级开发人员向有经验的开发人员看齐。当高级开发人员使用设计模式,初级开发人员也会跟着学。把你的组织建立成一个模式使用者的社区。
共享词汇的五种方式:
- 设计会议中:当你和你的团队在会议中讨论软件设计时,使用设计模式可以帮你们待在“设计”中久一点。从设计模式和面向对象原则的视角讨论设计,可以避免你的团队很快陷入实现的细节,也可以避免发生许多误解。
- 和其他开发人员:当你和其他开发人员讨论的时候,可以使用模式,这可以帮助其他开发人员学习新模式并建立一个社群。和别人分享你所学会的东西是很有成就感的一件事情。
- 在架构文档中:在你当你在编写架构文档的时候,使用模式将会缩减文档的篇幅,并且让读者更清晰的了解你的设计。
- 在代码注释以及命名习惯上:当你在编写代码的时候,应该在注释中清楚地注明你所使用的模式。在选择类和方法的名称时,应尽可能地显示出隐藏在下面的模式。其他的开发人员在阅读你的代码时会感激你,因为你让他们能够很快的了解你的实现。
- 将志同道合的开发人员结合在一起:分享你的知识。许多开发人员都听过模式,但并不真正了解什么是模式。你可以自愿给他们讲一堂模式介绍,或者成立一个读书会。
OO模式:
- 策略模式(Strategy):定义算法族,暴露相同的接口,分别封装起来,让它们之间可以相互替换使用。此模式让算法的变化独立于使用算法的客户。通常实现是定义一个Behaviour接口,这个接口里面只有一个run函数,然后不同具体算法实现这个Behaviour接口。
- 观察者模式(Observer):在对象之间定义一对多的依赖,当某个对象改变状态时,所以依赖它的对象都会受到通知更新。JDK中的java.util.Observable接口是一种通用的观察者模式实现。
- 装饰器模式(Decorator): 动态地将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另外一种选择。装饰器模式和代理模式有点相似,差别在于装饰器需要在原有实现上做扩展,而代理通常是用于中转请求。
- 抽象工厂模式(Abstract Factory): 提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。在抽象工厂内部,普遍使用了工厂方法模式。抽象工厂模式相比工厂方法,更强调里面各种create接口创建出来的对象之间的组合关系。
- 工厂方法模式(Factory Method): 定义一个创建对象的接口,但是由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类中。通常父类内部有个createX的抽象方法,返回某个抽象类X,而在子类中去实现createX这个方法,返回抽象类X的子类。
- 单件模式(Singleton): 确保一个类只有一个实例,并提供全局访问点。
- 命令模式(Command): 将请求封装成为对象,里面包含了完成这次请求的相关对象和上下文。通常这个对象有execute和undo两个方法,用于执行命令和撤销命令。
- 适配器模式(Adapter): 将一个类的接口,转换成为客户期望的另外一个接口。适配器让原本不兼容的类可以相互协作。
- 外观接口(Facade): 将子系统中多个接口的复杂调用,包装成为一个接口让外部使用。这样可以减少外部依赖性,并且更加易于使用。想象一个如果我们使用咖啡机做一杯卡布,“将加热水,将咖啡豆研磨,制作好咖啡,加热牛奶,打起奶泡,倒入咖啡”这几个步骤由一个按钮完成,是不是非常方便?
- 模板方法模式(Template Method): 在父类的一个方法中定义算法骨架,将其中一些步骤推迟到子类中完成。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。模板方法和策略有点类似,策略更强调算法之间可以相互替换,可以有很多策略而且策略通常都比较轻巧(只需要实现接口),而模板方法则强调算法骨架代码和其中可变代码的配合。JDK中最典型的例子就是实现Comparable接口来达到排序功能。
- 迭代器模式(Iterator): 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
- 组合模式(Composite): 允许将对象组成属性结构来表现“整体/部分”的层次结构。组合能让客户以一致的方式处理个别对象和对象组合。
- 状态模式(State): 允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
- 代理模式(Proxy): 为另外一个对象提供一个替身或占位符以访问这个对象。
下图是java.io.InputStream的类层次结构,FilterInputStream在实现上很明显使用了装饰器风格。在Java语言里面,装饰器通常需要继承有待装饰的类,然后在需要装饰的方法上增加自己的实现。
书中提到的其余的几个设计模式:
- 桥接模式(Bridge): 将实现和抽象放在两个不同的类层次中而使用它。
- 生成器模式(Builder): 将一个复杂对象的创建过程封装起来。相比工场模式,生成器模式产生对象单一,但是产生过程复杂。
- 责任链模式(Chain of Responsibility)
- 蝇量(Flyweight): 让某一个实例来提供多个虚拟实例。减少创建对象带来的开销,缺点是没有办法对某个虚拟实例做修改和扩展。
- 解释器(Interpreter): DSL
- 中介者模式(Mediator): 将相关对象之间的复杂的沟通和控制集中到一个类里面,缺点是中介者本身逻辑会特别复杂。
- 备忘录模式(Memento): 允许对对象状态进行序列化和反序列化
- 原型(Prototype): Javascript, Lua metatable
- 访问者模式(Visitor): 当遍历某个负责的数据结构时,由数据结构本身提供访问导游,在适当的节点上调用访问者的访问函数。比如XML的SAX, 访问者需要提供各种节点的handler.