设计模式学习笔记
设计模式(Design Pattern)是软件开发过程中,前人对反复出现的各种问题提出的优秀解决方案。本文是呉真在学习《Head First设计模式》过程中总结的一些学习笔记。
设计原则
- 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起
- 针对接口编程,而不是针对实现编程
- 多用组合,少用继承
- 为了交互对象之间的松耦合设计而努力
- 类应该对扩展开放,对修改关闭
- 要依赖抽象,不要依赖具体类
- 最少知识原则:只和你的密友谈话
- 好莱坞原则:别调用(打电话给)我们,我们会调用(打电话给)你
- 一个类应该只有一个引起变化的原因
策略(Strategy)模式
策略模式
定义了算法簇,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
举例:
汽车中有三大件:发动机、变速箱、底盘,不同型号的汽车对于三大件乃至其他小部件均有不同的组合,为此我们可以将三大件中通用的部分提取出来,用于组合不同型号的汽车。
- Engine(引擎接口)
- EA211(大众发动机实现)
- EA888(大众发动机实现)
- Gearbox(变速箱接口)
- 6AT(6档自动变速箱实现)
- 6MT(6档手动变速箱实现)
- CVT(无级变速变速箱实现)
- Chassis(底盘接口)
- FF(前置前驱实现)
- FR(前置后驱实现)
- 4WD(四轮驱动实现)
有了这些具体实现后,进一步搭配组合,产生了桑塔纳和TT两种型号的汽车(配置不一定真实,仅为举例说明),各自搭配了不同型号的配件。
- Car(汽车抽象类)
- Engine(属性,声明为接口)
- Gearbox
- Chassis
- Santana extends Car(大众·桑塔纳)
- Engine: EA211
- Gearbox: 6MT
- Chassis: FF
- TT extends Car(奥迪·TT)
- Engine: EA888
- Gearbox: 6AT
- Chassis: 4WD
观察者(Observer)模式
观察者模式
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
举例:
- 订阅某一本杂志时,出版社作为
主题
,而我们订阅者即为观察者
,每发行一期期刊,出版社都会将最新的杂志通过邮政系统送到我们的手里,这是一种推
模式。 - 关注某个公众号(
主题
)后,一旦这个公众号有更新,这个公众号的头像上就会出现一个小红点,此时我们(观察者
)点击进入这个公众号后,即可拉取最新的文章,这是一种拉
模式。
装饰器(Decorator)模式
装饰者模式
动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
举例:
购买一点点
奶茶时,我们要根据自身的需求,选择不同的糖分/加料/温度/大小,我们不可能针对每一种情况单独派生一个相应的子类,如此程序是相当难维护的。但是我们可以创建相应的糖分装饰器/调料装饰器,用来装饰每一种选择。
-
饮料(抽象类)
- 奶茶
- 奶绿
- 拿铁
- 可可
- 加料装饰器(抽象类)
- 波霸
- 布丁
- 珍珠
- 温度装饰器(抽象类)
- 热/温/冰/少冰/去冰
- 糖度装饰器(抽象类)
- 常规/半塘/微糖/去糖
- 大小装饰器(抽象类)
- 小杯/中杯/大杯
-
(大杯(常规糖(冰(珍珠(可可))))):一杯用珍珠、冰、常规糖、大杯装饰的可可
-
(中杯(微糖(温(波霸(奶绿))))):一杯用波霸、温、微糖、中杯装饰的奶绿
工厂(Factory)模式
简单工厂
:直接将实例化具体类的过程单独封装为一个简单工厂类。
静态工厂
:利用一个静态方法返回已实例化的具体类。
工厂方法模式
定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
抽象工厂模式
提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
单件(Singleton)模式
单件模式
确保一个类只有一个实例,并提供一个全局访问点。
命令(Command)模式
命令模式
将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
适配器(Adapter)模式
适配器模式
将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
外观(Facade)模式
外观模式
提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
模板方法(Template)模式
模板方法模式
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器(Iterator)模式
迭代器模式
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
- 迭代器允许访问聚合的元素,而不需要暴露它的内部结构
- 迭代器将遍历聚合的工作封装进一个对象中
- 当使用迭代器的时候,我们依赖聚合提供遍历
- 迭代器提供了一个通用的接口,让我们遍历聚合的项,当我们编码使用聚合的项时,就可以使用多态机制
组合(Composite)模式
组合模式
允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
- 组合模式提供一个接口,可同时包容个别对象和组合对象
- 组合模式允许客户对个别对象以及组合对象一视同仁
- 组合结构内的任意对象称为组件,组件可以是组合,也可以是叶结点
- 在实现组合模式时,有许多设计上的折衷。你要根据需要平衡透明性和安全性
状态(State)模式
状态模式
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
- 和程序状态机(PSM)不同,状态模式用类代表状态
代理(Proxy)模式
代理模式
为另一个对象提供一个替身或占位符以控制对这个对象的访问。
- 防火墙代理(Firewal Proxy):控制网络资源的访问,保护主体免于“坏客户”的侵害
- 智能引用代理(Smart Reference Proxy):当主体被引用时,进行额外的动作,例如计算一个对象被引用的次数
- 缓存代理(Caching Proxy):为开销大的运算结果提供暂时存储;它也允许多个客户共享结果,以减少计算或网络延迟
- 同步代理(Synchronization Proxy):在多线程的情况下为主题提供安全的访问
- 复杂隐藏代理(Complexity Hiding Proxy):用来隐藏一个类的复杂集合的复杂度,并进行访问控制
- 写入时复制代理(Copy-On-Write Proxy):用来控制对象的复制,方法是延迟对象的复制,直到客户真的需要为止
复合(Compound)模式
复合模式
结合两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题。
其他模式
- 桥接(Bridge):使用桥接模式不只改变你的实现,也改变你的抽象
- 生成器(Builder):使用生成器模式封装一个产品的构造过程,并允许按步骤构造
- 责任链(Chain Of Responsibility):当你想要让一个以上的对象有机会能够处理某个请求的时候,就使用责任链模式
- 蝇量(Flyweight):如想让某个类的一个实例能用来提供许多“虚拟实例”,就使用蝇量模式
- 解释器(Interpreter):使用解释器模式为语言创建监视器
- 中介者(Mediator):使用中介者模式来集中相关对象之间复杂的沟通和控制方式
- 备忘录(Memento):当你需要让对象返回之前的状态时,就使用备忘录模式
- 原型(Prototype):当创建给定类的实例的过程很昂贵或很复杂时,就使用原型模式
- 访问者(Visitor):当你想要为一个对象的组合增加新的能力,且封装并不重要时,就使用访问者模式