根据目的(模式是用来做什么的)可分为创建型(Creational),结构型(Structural)和行为型(Behavioral)三类:
创建型模式主要用于创建对象
结构型模式主要用于处理类或对象的组合
行为型模式主要用于描述类或对象如何交互和怎样分配职责
根据范围,即模式主要是处理类之间的关系还是处理对象之间的关系,可分为类模式和对象模式两种:
开闭原则的定义:软件实体应当对扩展开放,对修改关闭。
如何实现开闭原则:抽象化是开闭原则的关键,提供相对稳定的抽象层和灵活的具体层。
所有引用基类的地方必须能透明地使用其子类的对象
里氏代替原则的实现方法:
子类可以扩展父类的功能,但不能改变父类原有的功能。
高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象
我们在实际编程中只要遵循以下四点,就能在项目中满足依赖倒置原则:
⑴ 每个类尽量提供接口或抽象类,或者两者都具备。
⑵ 变量的声明类型尽量是接口或者是抽象类。
⑶ 任何类都不应该从具体类派生。
⑷ 使用继承时尽量遵循里氏替换原则。
单一职责原则是最简单的面向对象设计原则,用于控制类的粒度大小
一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中
客户端不应该依赖那些它不需要的接口
接口隔离原则的实现方法:
⑴ 接口尽量小,但是要有限度。
⑵ 只提供调用者需要的方法,屏蔽不需要的方法。
⑶ 了解环境,拒绝盲从。
⑷ 提高内聚,减少对外交互
迪米特法则又称为最少知识原则(Least Knowledge Principle, LKP)
每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位
合成复用原则又称为组合/聚合复用原则
优先使用对象组合,而不是继承来达到复用的目的。
简单工厂模式:违背了开闭原则,增加新的产品时需要修改工厂类。
工厂方法模式:符合开闭原则,增加新的产品只需对应增加一个新的具体工厂类,无需 修改源代码
抽象工厂模式:具有开闭原则的倾斜性,增加新的产品族符合开闭原则,增加新的产品12 等级结构违背开闭原则
Factory(工厂角色)
Product(抽象产品角色)
ConcreteProduct(具体产品角色)
工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
工厂模式又可称作虚拟构造器模式或多态工厂模式
Product(抽象产品)
ConcreteProduct(具体产品)
Factory(抽象工厂)
ConcreteFactory(具体工厂)
产品等级结构:产品等级结构即产品的继承结构
产品族:产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品
1、增加产品族
对于增加新的产品族,抽象工厂模式很好地支持了开闭原则,只需要增加具体产品并对应增加一个新的具体工厂,对已有代码无须做任何修改
2、增加新的产品等级结构
对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了开闭原则
AbstractFactory(抽象工厂)
ConcreteFactory(具体工厂)
AbstractProduct(抽象产品)
ConcreteProduct(具体产品)
Prototype(抽象原型类)
ConcretePrototype(具体原型类)
Client(客户类)
浅克隆(Shallow Clone):当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制
深克隆(Deep Clone):除了对象本身被复制外,对象所包含的所有成员变量也将被复制
确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例
某个类只能有一个实例
必须自行创建这个实例
必须自行向整个系统提供这个实例
Singleton(单例)
适配器模式让那些接口不兼容的类可以一起工作。别名为==包装器(Wrapper)==模式
Target(目标抽象类)
Adapter(适配器类)
Adaptee(适配者类)
public class Adapter extends Adaptee implements Target {
public void request() {
super.specificRequest();
}
}
缺省适配器模式(Default Adapter Pattern):当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况又称为单接口适配器模式
将目标类和适配者类解耦
增加了类的透明性和复用性
灵活性和扩展性非常好
类适配器模式:(1) 一次最多只能适配一个适配者类,不能同时适配多个适配者;(2) 适配者类不能为最终类;(3) 目标抽象类只能为接口,不能为类
对象适配器模式:在适配器中置换适配者类的某些方法比较麻烦
一致地对待容器对象和叶子对象
组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有部分-整体关系的层次结构。组合模式让客户端可以统一对待单个对象和组合对象。
Component(抽象构件)
Leaf(叶子构件)
Composite(容器构件)
装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责。就扩展功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案。
具体类和具体装饰类全部声明为抽象,但是具体装饰类不能够单独调用新增的方法,因为具体装饰类声明为抽象,不能够调用父类没有的方法(具体装饰类新增的方法)
对于客户端而言,具体构建对象和具体装饰对象没有任何区别
具体装饰类定义为具体的对象,而具体类定义为抽象
对客户端而言,具体构件类型是透明的无需关心,但是具体装饰类就必须指定类型,并且可以单独调用 新增的方法
装饰模式比继承更加灵活,不会导致类的个数急剧增加
通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为
可以对一个对象进行多次装饰
具体构件类与具体装饰类可以独立变化,符合开闭原则
模式缺点
使用装饰模式进行系统设计时将产生很多小对象
比继承更加易于出错,排错也更困难
Component(抽象构件)
ConcreteComponent(具体构件)
Decorator(抽象装饰类)
ConcreteDecorator(具体装饰类)
引入一个新的外观类(Facade)来负责和多个业务类(子系统)进行交互,而客户类只需与外观类交互
为多个业务类的调用提供了一个统一的入口,简化了类与类之间的交互,降低了系统的耦合度
为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。又称为门面模式,是迪米特法则的一种具体实现
外观类将客户端类与子系统内部复杂性分隔开
普通的外观模式,在子系统进行新增、删除、修改操作时,需要修改外观类的源代码,不符合开闭原则,所以需要将外观类抽象出来,在需要对子系统进行变化时,只需要新创建一个具体外观类即可
Facade(外观角色)
SubSystem(子系统角色)
行为型模式(Behavioral Pattern) 关注系统中对象之间的交互,研究系统在运行时对象之间的相互通信与协作,进一步明确对象的职责
行为型模式:不仅仅关注类和对象本身,还重点关注它们之间的相互作用和职责划分
使用继承关系在几个类之间分配行为,主要通过多态等方式来分配父类与子类的职责
使用对象的关联关系来分配行为,主要通过对象关联等方式来分配两个或多个类的职责
命令模式:将一个请求封装为一个对象
,从而让你可以用不同的请求对客户进行参数化
,对请求排队
或者记录请求日志
,以及支持可撤销的操作
。
将请求发送者和接收者完全解耦
发送者与接收者之间没有直接引用关系
发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求
对象行为型模式
别名为动作(Action)模式或事务(Transaction)模式
Command(抽象命令类)
ConcreteCommand(具体命令类)
Invoker(调用者)
Receiver(接收者)
降低系统的耦合度
新的命令可以很容易地加入到系统中,符合开闭原则
可以比较容易地设计一个命令队列
为**请求的撤销(Undo)和恢复(Redo)**操作提供了一种设计和实现方案
使用命令模式可能会导致某些系统有过多的具体命令类(针对每一个对请求接收者的调用操作都需要设计一个具体命令类)
模式适用环境
迭代器模式:提供一种方法顺序访问一个聚合对象中各个元素,且不用暴露该对象的内部表示。
对象行为型模式
又名游标(Cursor)模式
通过引入迭代器,客户端无须了解聚合对象的内部结构即可实现对聚合对象中成员的遍历
,还可以根据需要很方便地增加新的遍历方式
Iterator(抽象迭代器)
ConcreteIterator(具体迭代器)
Aggregate(抽象聚合类)
ConcreteAggregate(具体聚合类)
增加一个新的具体聚合类
,只需增加一个新的聚合子类和一个新的具体迭代器类即可,原有类库代码无须修改,符合开闭原则更换一个迭代器
,只需要增加一个新的具体迭代器类作为抽象迭代器类的子类,重新实现遍历方法即可,原有迭代器代码无须修改,也符合开闭原则迭代器中增加新的方法
,则需要修改抽象迭代器的源代码,这将违背开闭原则优点
支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式简化了聚合类
由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,符合开闭原则
缺点
在增加新的聚合类时需要对应地增加新的迭代器类,类的个数成对增加
,这在一定程度上增加了系统的复杂性
抽象迭代器的设计难度较大
,需要充分考虑到系统将来的扩展。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是一件很容易的事情
观察者模式(Observer Pattern):定义对象之间的一种一对多
依赖关系,使得每当一个对象状态发生改变
时,其相关依赖对象都得到通知并被自动更新
对象行为型模式
别名
发布-订阅(Publish/Subscribe)模式
模型-视图(Model/View)模式
源-监听器(Source/Listener)模式
从属者(Dependents)模式
Subject(目标)
ConcreteSubject(具体目标)
Observer(观察者)
ConcreteObserver(具体观察者)
可以实现表示层和数据逻辑层的分离
在观察目标和观察者之间建立一个抽象的耦合
支持广播通信,简化了一对多系统设计的难度
符合开闭原则
将所有的观察者都通知到会花费很多时间
如果存在循环依赖时可能导致系统崩溃
没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而只是知道观察目标发生了变化
一个抽象模型有两个方面,其中一个方面依赖于另一个方面
一个对象的改变将导致一个或多个其他对象发生改变,且并不知道具体有多少对象将发生改变,也不知道这些对象是谁
需要在系统中创建一个触发链
状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为
。
对象看起来似乎修改了它的类。
对象行为型模式,又名状态对象(Objects for States)
用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题
将一个对象的状态从该对象中分离出来
对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态
Context(环境类)
State(抽象状态类)
ConcreteState(具体状态类)
优点
封装了状态的转换规则
将所有与某个状态有关的行为放到一个类中
只需要注入一个不同的状态对象即可使环境对象拥有不同行为
允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块
可以让多个环境对象共享一个状态对象
缺点
会增加系统中类和对象的个数,导致系统运行开销增大
结构与实现都较为复杂,如果使用不当将导致程序结构和代码混乱,增加系统设计的难度
对开闭原则的支持并不太好,增加新的状态类需要修改负责状态转换的源代码,否则无法转换到新增状态
;而且修改某个状态类的行为也需要修改对应类的源代码
对象的行为依赖于它的状态(例如某些属性值),状态的改变将导致行为的变化
在代码中包含大量与对象状态有关的条件语句
策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法可以独立于使用它的客户变化
对象行为型模式
又称为政策(Policy)模式
每一个封装算法的类称之为策略(Strategy)类
策略模式提供了一种可插入式(Pluggable)算法的实现方案
提供了对开闭原则的完美支持
提供了管理相关的算法族的办法
提供了一种可以替换继承关系的办法
可以避免多重条件选择语句
提供了一种算法的复用机制,不同的环境类可以方便地复用策略类
客户端必须知道所有的策略类,并自行决定使用哪一个策略类
将造成系统产生很多具体策略类
无法同时在客户端使用多个策略类
一个系统需要动态地在几种算法中选择一种
避免使用难以维护的多重条件选择语句
不希望客户端知道复杂的、与算法相关的数据结构,提高算法的保密性与安全性
策略模式包含以下3个角色:
Context(环境类)
Strategy(抽象策略类)
ConcreteStrategy(具体策略类)
模板方法模式:定义一个操作中算法的框架,而将一些步骤延迟到子类中
。模板方法模式使得子类不改变一个算法的结构即可重定义该算法的某些特定步骤
类行为型模式,是一种基于继承的代码复用技术
将一些复杂流程的实现步骤封装在一系列基本方法中
相同的算法框架可以有不同的执行结果
模板方法模式包含以下两个角色:
AbstractClass(抽象类)
ConcreteClass(具体子类)
在父类中定义一个算法,在子类实现算法时并不会改变算法中步骤的执行次序
提取了类库中的公共行为,将公共行为放在父类中
,而通过其子类来实现不同的行为
可实现一种反向控制结构
,通过子类覆盖父类的钩子方法来决定某一特定步骤是否需要执行
更换和增加新的子类很方便,符合单一职责原则和开闭原则
需要为每一个基本方法的不同实现提供一个子类
,如果父类中可变的基本方法太多,将会导致类的个数增加,系统会更加庞大,设计也更加抽象
一次性实现一个算法的不变部分,并将可变的行为留给子类来实现
各子类中公共的行为应被提取
出来,并集中到一个公共父类中,以避免代码重复
需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制