设计模式(行为型)
设计模式 --行为型
范围\目的 |
创建型模式 |
结构型模式 |
行为型模式 |
类模式 |
工厂方法模式 |
(类)适配器模式 |
解释器模式 模板方法模式 |
对象模式 |
抽象工厂模式 建造者模式 原型模式 单例模式 |
(对象)适配器模式 桥接模式 组合模式 装饰模式 外观模式 享元模式 代理模式 |
职责链模式 命令模式 迭代器模式 中介者模式 备忘录模式 观察者模式 状态模式 策略模式 访问者模式 |
根据目的、用途的不同,分为创建性模式、结构性模式、行为性模式。创建型模式主要用于创建对象,结构型模式主要用于处理类和对象的组合,行为性模式主要用于描述类或对象的交互以及职责分配。
根据处理范围不同,设计模式又可分为类模式和对象模式,类模式处理类与子类的关系,通过处理这些关系来建立继承,属于静态关系,在编译时候确定下来;对象模式处理对象之间的关系,运行时发生变化,属于动态关系。
3.行为性模式Behavioral(11种)
是在不同的对象之间来划分责任和算法的抽象形式,不仅仅是关于类和对象的,也是关于类和对象之间是如何作用的,同样也分为类行为模式和对象行为模式,类行为模式,是使用继承关系在几个类之间分配它的功能;对象行为模式,是使用对象的聚合/组合来分配行为
一. 职责链模式(对象行为性模式)
定义:通过给多个对象处理请求的机会,减少请求的发送者与接收者之间的耦合。将接收对象链接起来,在链中传递请求,直到有一个对象处理这个请求
不保证每个请求都被接受,因为一个请求没有一个明确的接收者的时候,不能保证一定会被处理,就会一直被传递下去
职责链模式包含如下角色:
- Handler: 抽象处理者
- ConcreteHandler: 具体处理者
- Client: 客户类
在以下情况下可以使用职责链模式:
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可动态指定一组对象处理请求。
二. 命令模式(对象行为性模式)
定义:将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化,将请求排队或记录请求日志,支持可撤销的操作
是一种对象行为性模式,类似于传统设计方法的回调机制,将一个请求封装为一个对象,可用不同的请求对客户进行参数化,将请求排队或记录请求日志,这样做的目的就可以支持可撤销的操作。对这个命令的封装,将发出命令的责任和执行命令的责任分开,委派给不同的对象,这样就能实现发送者和接收者的完全的解耦,以达到松耦合的目的,提高系统的可扩展性和灵活性
命令模式包含如下角色:
- Command: 抽象命令类
- ConcreteCommand: 具体命令类
- Invoker: 调用者
- Receiver: 接收者
- Client:客户类
典型的调用者代码:
public class Invoker { private Command command; public Invoker(Command command) { this.command=command; } public void setCommand(Command command) { this.command=command; }
//业务方法,用于调用命令类的方法
public void call() { command.execute(); } }
典型的具体命令类代码:
public class ConcreteCommand extends Command { private Receiver receiver; public void execute() { receiver.action(); } }
典型的请求接收者代码:
public class Receiver { public void action() { //具体操作 } }
三.解释器模式(类行为性模式)
定义:给定一种语言,定义它的文法表示,并定义一个解释器,该解释器用来根据文法表示来解释语言中的句子
四. 迭代器模式(对象行为性模式)
定义:提供一种方法来顺序访问一个聚合对象中的各个元素,而不需要暴露该对象的内部表示。
迭代器模式支持以不同的方式遍历一个聚合对象,复杂的聚合可以采用多种方法遍历,允许在同一个聚合上有多个遍历,每个迭代器保持自己的遍历状态,这样可以支持同时进行多个遍历操作
迭代器模式包含如下角色:
Iterator: 抽象迭代器
ConcreteIterator: 具体迭代器
Aggregate: 抽象聚合类
ConcreteAggregate: 具体聚合类
模式适用环境
在以下情况下可以使用迭代器模式:
访问一个聚合对象的内容而无须暴露它的内部表示。
需要为聚合对象提供多种遍历方式。
为遍历不同的聚合结构提供一个统一的接口。
五.中介者模式(对象行为性模式)
定义:用一个中介对象来封装一系列的对象交互。它使各对象不需要显示地相互调用,从而达到低耦合,还可以独立地改变对象间的交互
中介对象的存在,就保证了对象结构上的稳定,也就是说,系统结构不会因为新的对象引用而导致大量的修改。
中介者模式包含如下角色:
- Mediator: 抽象中介者
- ConcreteMediator: 具体中介者
- Colleague: 抽象同事类
- ConcreteColleague: 具体同事类
中介者模式可以使对象之间的关系数量急剧减少:
中介者承担两方面的职责:
中转作用(结构性):通过中介者提供的中转作用,各个同事对象就不再需要显式引用其他同事,当需要和其他同事进行通信时,通过中介者即可。该中转作用属于中介者在结构上的支持。
协调作用(行为性):中介者可以更进一步的对同事之间的关系进行封装,同事可以一致地和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。该协调作用属于中介者在行为上的支持。
典型的抽象中介者类代码:
public abstract class Mediator { protected ArrayList colleagues; public void register(Colleague colleague) { colleagues.add(colleague); } }
典型的具体中介者类代码:
public abstract void operation(); }
public class ConcreteMediator extends Mediator { public void operation() { ...... ((Colleague)(colleagues.get(0))).method1(); ...... } }
典型的抽象同事类代码:
public abstract class Colleague { protected Mediator mediator; public Colleague(Mediator mediator) { this.mediator=mediator; } public abstract void method1(); public abstract void method2(); }
典型的具体同事类代码:
public class ConcreteColleague extends Colleague { public ConcreteColleague(Mediator mediator) { super(mediator); } public void method1() { ...... } public void method2() { mediator.operation1(); } }
在以下情况下可以使用中介者模式:
系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解。
一个对象由于引用了其他很多对象并且直接和这些对象通信,导致难以复用该对象。
想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。可以通过引入中介者类来实现,在中介者中定义对象交互的公共行为,如果需要改变行为则可以增加新的中介者类。
六. 备忘录模式
定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,从而可以在以后将该对象恢复到原先保存的状态
这样做,就可以在以后,将这个对象恢复到原来的保存状态。
备忘录模式包含如下角色:
- Originator: 原发器
- Memento: 备忘录
- Caretaker: 负责人
模式分析
由于在备忘录中存储的是原发器的中间状态,因此需要防止原发器以外的其他对象访问备忘录。
备忘录对象通常封装了原发器的部分或所有的状态信息,而且这些状态不能被其他对象访问,也就是说不能在备忘录对象之外保存原发器状态,因为暴露其内部状态将违反封装的原则,可能有损系统的可靠性和可扩展性。
为了实现对备忘录对象的封装,需要对备忘录的调用进行控制:
- 对于原发器而言,它可以调用备忘录的所有信息,允许原发器访问返回到先前状态所需的所有数据;
- 对于负责人而言,只负责备忘录的保存并将备忘录传递给其他对象;
- 对于其他对象而言,只需要从负责人处取出备忘录对象并将原发器对象的状态恢复,而无须关心备忘录的保存细节。
理想的情况是只允许生成该备忘录的那个原发器访问备忘录的内部状态。
典型的原发器类代码如下所示:
public class Originator { private String state; public Originator(){} // 创建一个备忘录对象 public Memento createMemento(){ return new Memento(this); } // 根据备忘录对象恢复原发器状态 public void restoreMemento(Memento m){ state = m.state; } public void setState(String state) { this.state=state; } public String getState() { return this.state; } }
典型的备忘录类代码如下所示:
class Memento { private String state; public Memento(Originator o){ state = o.state; } public void setState(String state) { this.state=state; } public String getState() { return this.state; } }
典型的负责人类代码如下所示:
public class Caretaker { private Memento memento; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento=memento; } }
七. 观察模式(对象行为性模式)
定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新
又被称为发布-订阅模式,命令模式的优点,在于实现了表示层和数据层的分离,并且定义了稳定地更新消息的传递机制,类别很清晰,对接口也进行了抽象,使得相同的数据层可以有不同的表示层,mvc就属于这种。
在以下情况下可以使用观察者模式:
一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
一个对象必须通知其他对象,而并不知道这些对象是谁。
需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
MVC模式
MVC模式是一种架构模式,它包含三个角色:模型(Model),视图(View)和控制器(Controller)。观察者模式可以用来实现MVC模式,观察者模式中的观察目标就是MVC模式中的模型(Model),而观察者就是MVC中的视图(View),控制器(Controller)充当两者之间的中介者(Mediator)。当模型层的数据发生改变时,视图层将自动改变其显示内容。
八.状态模式(对象行为性模式)
在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的 (stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的。当一个这样的对象与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化
定义:允许一个对象在其内部状态改变时能够改变它的行为
从表面上看,对象好像修改了它的类,状态模式封装了状态的转换过程,但是它需要事先枚举状态的种类,这样事先确定状态的种类,导致在状态模式中新加状态时,可能会违反开闭原则。对新的状态的引用,需要修改与它能够转换的其它状态类的代码,而且状态类的使用,会增加类和对象的个数。
状态模式包含如下角色:
- Context: 环境类
- State: 抽象状态类
- ConcreteState: 具体状态类
模式分析
状态模式描述了对象状态的变化以及对象如何在每一种状态下表现出不同的行为。
状态模式的关键是引入了一个抽象类来专门表示对象的状态,这个类我们叫做抽象状态类,而对象的每一种具体状态类都继承了该类,并在不同具体状态类中实现了不同状态的行为,包括各种状态之间的转换。
在状态模式结构中需要理解环境类与抽象状态类的作用:
环境类实际上就是拥有状态的对象,环境类有时候可以充当状态管理器(State Manager)的角色,可以在环境类中对状态进行切换操作。
抽象状态类可以是抽象类,也可以是接口,不同状态类就是继承这个父类的不同子类,状态类的产生是由于环境类存在多个状态,同时还满足两个条件:这些状态经常需要切换,在不同的状态下对象的行为不同。因此可以将不同对象下的行为单独提取出来封装在具体的状态类中,使得环境类对象在其内部状态改变时可以改变它的行为,对象看起来似乎修改了它的类,而实际上是由于切换到不同的具体状态类实现的。由于环境类可以设置为任一具体状态类,因此它针对抽象状态类进行编程,在程序运行时可以将任一具体状态类的对象设置到环境类中,从而使得环境类可以改变内部状态,并且改变行为。
状态模式的优点
封装了转换规则。
枚举可能的状态,在枚举状态之前需要确定状态种类。
将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
状态模式的缺点
状态模式的使用必然会增加系统类和对象的个数。
状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。
在以下情况下可以使用状态模式:
对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为。
代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。在这些条件语句中包含了对象的行为,而且这些条件对应于对象的各种状态。
九. 策略模式(对象行为性模式)
定义:定义一系列算法,把它们一个个封装起来,并且使它们之间可相互替换,从而让算法可以独立于使用它的用户而变化
目的是使行为和环境相分隔,当出现新的行为时,只需要实现新的策略类。
策略模式包含如下角色:
- Context: 环境类
- Strategy: 抽象策略类
- ConcreteStrategy: 具体策略类
抽象策略类
public abstract class AbstractStrategy
{
public abstract void algorithm();
}
具体策略类
public class ConcreteStrategyA extends AbstractStrategy
{
public void algorithm()
{
//算法A
}
}
环境类
public class Context
{
private AbstractStrategy strategy;
public void setStrategy(AbstractStrategy strategy)
{
this.strategy= strategy;
}
public void algorithm()
{
strategy.algorithm();
}
}
客户端代码
Context context = new Context();
AbstractStrategy strategy;
strategy = new ConcreteStrategyA();
context.setStrategy(strategy);
context.algorithm();
策略模式的缺点
客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。
在以下情况下可以使用策略模式:
如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
一个系统需要动态地在几种算法中选择一种。
如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。
策略模式与状态模式
可以通过环境类状态的个数来决定是使用策略模式还是状态模式。
策略模式的环境类自己选择一个具体策略类,具体策略类无须关心环境类;而状态模式的环境类由于外在因素需要放进一个具体状态中,以便通过其方法实现状态的切换,因此环境类和状态类之间存在一种双向的关联关系。
使用策略模式时,客户端需要知道所选的具体策略是哪一个,而使用状态模式时,客户端无须关心具体状态,环境类的状态会根据用户的操作自动转换。
如果系统中某个类的对象存在多种状态,不同状态下行为有差异,而且这些状态之间可以发生转换时使用状态模式;如果系统中某个类的某一行为存在多种实现方式,而且这些实现方式可以互换时使用策略模式。
十. 模板模式(类行为性模式)
定义:定义一个操作中的算法骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重新定义算法中某些特定步骤
缺点是对不同的实现都需要定义一个子类,导致类的个数会增加,但是更符合类的职责分配的原则,使类的内聚性提高
十一.访问者模式(对象行为性模式)
定义:表示一个作用于某对象结构中的各元素的操作,使得在不改变各元素的类的前提下定义作用于这些元素的新操作
使得增加新的操作很容易,但是牺牲了类的封装性。
模式动机
在实际使用时,对同一集合对象的操作并不是唯一的,对相同的元素对象可能存在多种不同的操作方式。
而且这些操作方式并不稳定,可能还需要增加新的操作,以满足新的业务需求。
此时,访问者模式就是一个值得考虑的解决方案。
访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式,这就是访问者模式的模式动机。
访问者模式包含如下角色:
Vistor: 抽象访问者
ConcreteVisitor: 具体访问者
Element: 抽象元素
ConcreteElement: 具体元素
ObjectStructure: 对象结构
典型的抽象访问者类代码如下所示:
public abstract class Visitor
{
public abstract void visit(ConcreteElementA elementA);
public abstract void visit(ConcreteElementB elementB);
public void visit(ConcreteElementC elementC)
{
//元素ConcreteElementC操作代码
}
}
典型的具体访问者类代码如下所示:
public class ConcreteVisitor extends Visitor
{
public void visit(ConcreteElementA elementA)
{
//元素ConcreteElementA操作代码
}
public void visit(ConcreteElementB elementB)
{
//元素ConcreteElementB操作代码
}
}
典型的抽象元素类代码如下所示:
public interface Element
{
public void accept(Visitor visitor);
}
典型的具体元素类代码如下所示:
public class ConcreteElementA implements Element
{
public void accept(Visitor visitor)
{
visitor.visit(this);
}
public void operationA()
{
//业务方法
}
}
典型的对象结构类代码如下所示:
public class ObjectStructure
{
private ArrayList list=new ArrayList();
public void accept(Visitor visitor)
{
Iterator i=list.iterator();
while(i.hasNext())
{
((Element)i.next()).accept(visitor);
}
}
public void addElement(Element element)
{
list.add(element);
}
public void removeElement(Element element)
{
list.remove(element);
}
}
在以下情况下可以使用访问者模式:
一个对象结构包含很多类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。访问者模式使得我们可以将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。
对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
状态图实例: