行为型模式:关注系统中对象之间的交互,以及相互通信协作,进一步明确对象的职责
类行为型模式:通过多态来分配父子的职责。
对象行为型模式:通过对象关联等方式职责分配职责。
职责链模式
职责链模式:将请求的发送者与接收者解耦,让接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个能处理它为止。
职责链数据结构
抽象处理者(Handler):定义一个处理请求的接口,关联一个抽象处理者类型的对象作为对下家的引用,由此构成一条链
具体处理者(ConcreteHandler):实现抽象处理者处理用户请求,处理请求之前需要检查是否有权限,如果可以处理就处理,否则进行转发。
职责链模式实现
## 定义抽象处理者
public abstract class Handler{
protected Handler successor;
public void setSuccessor(Handler successor){
this.successor=successor;
}
public abstract void handleRequest(String request);
}
## 定义具体处理者
public class ConcereteHandler extends Handler{
public void handleRequest(String request){
if(满足某条件){
//处理请求
}else{
//转发请求
this.successor.handleRequest(request);
}
}
}
职责链优缺点
优点:
- 客户端无需知道哪一个对象处理它的请求
- 请求处理对象维持一个指向后继者的引用,简化对象的连接
- 可通过在运行时对该链进行动态增加or删除改变处理一个请求的职责
- 系统增加具体处理者无需修改原有代码,只需要客户端重新创建链即可,满足开闭原则
缺点:
a)没有明确接收者,不能保证被处理
b)对请求链长的职责链,系统性能收到影响
职责链适用环境
例如:web应用中创建多个过滤器Filter链来对请求进行过滤
工作流系统中实现办公的分级审批
异常处理机制,不同的catch子句构成了一条处理异常对象的职责链
- 有多个对象可以处理请求
- 在不明确指定接收者的情况下向多个对象中的一个提交一个请求
- 客户端动态指定一组对象处理请求,而且还可以改变链中的执行顺序
命令模式
它将请求发送者,请求接收者解耦,可以让相同的发送者对应不同的接收者。
命令模式定义:将一个请求封装成一个对象,从而通过不同的请求将客户端参数化,实现了请求排队,记录请求日志,可撤销操作
命令模式数据结构
- 抽象命令类(Command):其中声明了执行请求的execute()方法,调用接收者的相关操作
- 具体命令类(ConcereteCommand):对应具体的接收者对象,将接收者对象的动作绑定其中、
- 调用者(Invoker):请求发送者,它通过命令对象执行请求【设计时不需要指定接收者,运行时将具体命令对象注入,然后调用命令对象的excute方法,从而实现间接调用请求接收者的相关词操作】
- 接收者(Receiver):接收者执行与请求相关操作,具体实现对请求的业务处理
命令模式的实现
## 定义抽象命令类
public abstract class Command{
public abstract void excute();
}
## 定请求发送者,也就是调用者
public class Invoker{
private Command command;
public Invoker(Command command){
this.command=command;
}
//业务方法
public void call{
command.execute();
}
}
## 定义具体命令类
public class ConcereteCommand extends Command{
private Receiver receiver; //维持一个对请求接收者的引用
//调用请求接收者的也无法方法
public void excute(){
receiver.action();
}
}
## 定义请求接收者
public clas Receiver{
public void action(){
//具体操作
}
}
实现命令队列
请求发送者发送一个请求会有多个请求接收者产生响应。
- 可以对一组命令进行批量处理,从而设计批量处理程序。
- 如果请求接收者对请求次序没有要求,还可以使用多线程技术来并发调用对象的excute方法,提高执行效率
## 增加一个命令队列
public class CommandQueue{
private List<Command> commands=new ArrayList<Command>();
public addCommand(Command command){commands.add(command)}
public void excute(){
for(Object command:commands){
command.excute();
}
}
}
## 重写调用者
public class Invoker{
private CommandQueue commandQueue; //维护commandQueue的引用
public Invoker(CommandQueue commandQueue){
this.commandQueue=commandQueue;
}
public void call(){
commandQueue.excute();
}
}
记录请求日志-实现撤销功能-宏命令
命令模式优缺点
优点
- 系统通过引入抽象命令,将请求者和接收者实现完全解耦
- 系统新增新得命令不会修改原有代码
- 比较容易设计一个命令队列,宏命令
- 为请求的撤销和恢复提供了可行方案
缺点
导致系统增加过多具体命令类,因为对每个请求接收者调用都需要设计一个具体命令类
命令模式适用环境
- 系统需要将请求调用者和请求接收者解耦
- 系统需要在不同的时间执行请求
- 系统需要支持命令的撤销和恢复
- 系统需要将一组操作组合成宏命令
迭代器模式
用于对一个聚合对象进行遍历,可以将数据的遍历从聚合对象中分离出来,聚合对象只负责存储数据,而遍历数据有迭代器完成
迭代器模式定义:提供一种方法顺序访问一个聚合对象中的各个元素,而不用暴露该对象的内部表示
迭代器数据结构
- 抽象迭代器(Iterator):定义访问和遍历元素的接口
- 具体迭代器(ConcereteIterator):实现抽象迭代器,通过游标记录在集合对象所处的位置
- 抽象聚合类(Aggrate):用于存储和管理元素对象,充当抽象迭代器工厂角色
- 具体聚合类(ConcereteAggrate):实现抽象聚合类中创建迭代器的方法
迭代器模式的实现
## 定义抽象迭代器
public interface Interator{
public void first();
public void next();
public boolean hasNext();
}
## 具体迭代器
public class ConcreteInterator implements Interator{
private ConcreteAggrage objects; //维持一个对具体聚合对象的引用
private int cursor; //游标,记录当前访问位置
public ConcreteInterator(ConcreteAggrate objects){
this.objects=objects;
}
public void first(){};
public void next(){};
public boolean hasNext(){};
}
## 定义存储数据的聚合对象
public interface Aggrate{
Interator createInterator();
}
## 定义具体迭代器
public class ConcreteAggrate implements Aggrate{
public Interagor createInterator(){
return new ConcreteInterator(this);
}
}
内部类实现迭代器
将迭代器设知道聚合类的内部
public abstract class AbstractlList<E> extends AbstractCollection<E> implements List<E>{
private class Int implements Interator<E>{
int cursor=0;
..
}
}
java内置迭代器
在每一个next()方法被调用时,迭代器游标由元素1——>元素2,因此next方法返回元素2的引用
迭代器优缺点
优点
- 客户端可以通过不同的方式遍历一个聚合对象(更换迭代器),在同一个聚合对象上可定义多种遍历方式
- 系统简化了聚合类的设计
- 增加聚合类和迭代器都无需修改现有代码
缺点
增加新得聚合类需要增加对应迭代器类,类的个数成对增加
迭代器适用环境
- 客户端访问一个聚合对象,而无需暴露它的内部表示
- 系统需要为聚合对象提供多种管理方式
- 客户端使用统一接口遍历聚合对象,而且实现类为其提供不同实现
观察者模式
定义对象之间的额一种一对多的依赖关系,使得每当一个对象的装填发生变化时,其他依赖对象都得到通知并被自动更新。类似发布订阅模式。
观察者模式数据结构
- 目标(Subject):它是被观察的对象,定义了一个观察者的集合,提供一系列增删观察者对象的方法,同时有定义了通知方法notify()
- 具体目标(ConcreteSubject):封装了数据,当数据发送改变时将向它的各个观察者发出通知
- 观察者(Observer):观察者对观察目标的改变做出反应,一般声明更新数据的方法update
- 具体观察者(ConcreteObserver):维护一个具体目标的引用,他存储具体观察者的有关状态
观察者模式实现
观察者描述了如何建立 对象和对象之间的依赖关系,以及如何构造满足这种需求的系统。
目标——>观察者
## 定义抽象目标类
public abstract class Subject{
//存储所有观察者对象
protected List<Observer> observers=new ArrayList();
//注册观察者
public void attach(Observer observer){
observers.add(observers);
}
//注销观察者
public void detach(Observer observer){
observers.remove(observer);
}
//通知方法
public abstract void notify();
}
## 具体目标类
public class ConcreteSubject extends Subject(){
public void notify(){
//遍历观察者集合,调用每个观察者的响应方法
for(Object obj:observers){
(Observers)obj.update();
}
}
}
## 抽象观察者
public interface Observer{
//定义响应方法
public void update();
}
## 具体观察者
public class ConcreteObserver implements Observer{
public void update(){
//具体响应代码
}
}
JDK对观察者模式的支持
java.util包中提供了一个Observable类以及Observe接口,它们构成了JDK对观察者模式支持的基础
目标——>观察者
//观察者
public interface Observer {
void update(Observable o, Object arg); //当观察目标发生变化,此方法会被调用
}
//目标
public class Observable {
private boolean changed = false;
private Vector<Observer> obs = new Vector(); //存储目标的观察者对象
public synchronized void addObserver(Observer var1) { //增加观察者
if (var1 == null) {
throw new NullPointerException();
} else {
if (!this.obs.contains(var1)) {
this.obs.addElement(var1);
}
}
}
public synchronized void deleteObserver(Observer var1) { //删除观察者
this.obs.removeElement(var1);
}
public void notifyObservers() { //通知观察者
this.notifyObservers((Object)null);
}
public void notifyObservers(Object var1) { //通知观察者
Object[] var2;
synchronized(this) {
if (!this.changed) {
return;
}
var2 = this.obs.toArray();
this.clearChanged();
}
for(int var3 = var2.length - 1; var3 >= 0; --var3) {
((Observer)var2[var3]).update(this, var1);
}
}
public synchronized void deleteObservers() { //删除观察者
this.obs.removeAllElements();
}
protected synchronized void setChanged() { //表示目标状态发生了变化
this.changed = true;
}
protected synchronized void clearChanged() { //目标对象不再变化,目标已经通知了所有观察者
this.changed = false;
}
public synchronized boolean hasChanged() { //判断目标是否变化
return this.changed;
}
public synchronized int countObservers() { //返回目标的观察者数目
return this.obs.size();
}
}
观察者模式与Java事件的处理
JDK1.0的事件模型是基于职责链模式,但是这种模型不适于复杂系统
JDK1.1的事件模型是基于观察者模式的委派事件模型=引发的事件不由引发事件的对象自己处理,而是委派给独立事件处理对象负责。
委派事件模型={事件源+事件+事件监听器}:
事件源充当目标角色:发布事件
事件监听器充当观察者角色 :向目标订阅感兴趣的事件
事件对象封装事件相关信息
观察者与MVC
MVC是一种架构模型,包含三个角色:模型+视图+控制器
模型对应于观察目标
视图对应于观察者
控制器充当两者之间的中介者
当模型层的数据发生改变时,视图层的数据将自动改变其显示内容。
java事件原理示例
单击按钮流程
- 单击按钮,触发ActionEvent类型的事件,产生ActionEvent类型的事件对象
- 将ActionEvent事件对象传递给事件监听器对象,开发人员实现ActionListener接口,重写actionPerformed方法处理事件
- 开发人员将ActionListener接口的实现类注册到按钮中
- JVM触发事件调用按钮的方法,内部调用事件监听器的事件处理方法
自定义GUI组件:包含两个文本框和两个按钮的登陆组件LoginBean
## 自定义事件,AWT模型继承EventObject
public class LoginEvent{
String username;
String password;
}
## 自定义登陆组件(目标类) 一对一观察
public class LoginBean{
LoginEventListener listener;
LoginEvent event;
//
public void addLoginEventListener(LoginEventListener listener){};
public void fireLoginEvent(Object obj,String username,String password){
//实例化事件对象
//将事件传递给观察者对象 传递对象—通过方法参数
//调用观察者对象的响应方法vlidateLogin(event)
};
}
## 自定义抽象观察者
public abstract class LoginEventListener{
validateLogin(LoginEvent event); //事件处理函数
}
## 自定义具体观察者
public class LoginValidateA{} //为事件提供不同实现
public class LoginValidateB() //为事件提供不同实现
观察者模式优缺点
凡是使用1对1,1对多的交互场景都可以使用观察者模式
优点
- 系统定义了完整的消息更新传递机制,抽象了更新接口
- 系统支持广播通信,简化1对多系统设计难度
- 增加新得具体观察者无需修改原有代码,符合开闭原则
缺点
a)若目标拥有很多观察者,将所有观察者通知到浪费时间
b) 若观察者和观察目标之间存在循环依赖,则可能导致系统奔溃
c) 观察者不知道目标是怎么发生变化的
观察者使用场景
- 抽象模型有两个方便,一个依赖于另一个,将两个方面封装在独立对象使其可独立扩展
- 需要在系统创建一个触发链
- 一个对象的改变将导致一个或多个对象也发生改变,而并不知道具体有多少对象发生改变,也不知道这些对象是谁
策略模式
通过算法定义和算法使用的分离,实现了算法的自由切换或新增,支持用户从算法族中更换算法或新增算法
策略模式定义:定义一系列算法,将每一个算法封装起来,并可随意切换或新增
策略模式数据结构
- 环境类(Context):定义采用哪种算法的逻辑
- 抽象策略类(Stragtegy):为支持的算法声明了抽象方法
- 具体策略类(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(); //调用具体策略类的算法
}
}
策略模式优点
通过关联代替继承
优点:
- 系统新增算法不会修改原有代码
- 系统提供了关系相关算法族的方法
- 客户端避免了多重条件选择语句,把选择和算法解耦,符合单一原则
缺点:
a) 客户端必须知道所有策略类,并自主选择哪一种策略类
b) 导致系统增加很多具体策略类
c) 无法同时使用多个策略类
策略模式适用环境
- 系统需要在几种算法选择一种
- 一个对象有很多种行为,避免采用多重条件判断
- 系统不希望用户知道算法的细节
模板方法模式
模板方法模式定义:定义一个操作中算法的框架,而将一些步骤延迟到子类中。模范方法模式可以在不改变一个算法的结构的基础上重定义该算法的某些特定步骤
模板方法数据结构
- 抽象类(AbstractClass):定义一系列基本操作,每一个基本操作对应算法的一个步骤,子类可重写这些步骤;在抽象类中实现了一个模板方法,将基本操作方法组合形成一个总算法框架
- 抽象子类(ConcreteClass):实现父类中定义的一些特性算法步骤
模板方法的实现
## 实现模板方法
public abstract class AbstractClass{
//模板方法
public void templateMethod(){
operation1();
operation2();
operation3();
}
public void operation1(){};//具体方法
public abstract void operation2(){};//抽象方法
public void operation3(){};//钩子方法
}
## 具体子类 多态性:子类对象在运行时将覆盖父类对象
public class ConcreteClass extends AbstractClass{
public void operation1(){
//重写操作1
}
public void operation2(){
//重写操作2
}
}
模板模式优缺点
优点
- 系统中通过父类形式化定义一个算法框架,通过子类实现具体细节
- 系统中可以通过子类覆盖钩子函数,来决定某一特定步骤是否需要执行
缺点
a) 模板方法为每一个基本方法的不同实现都提供一个子类,导致个数急剧增加
模板模式适用环境
- 对一些复杂算法进行分割,将算法中固定不变的步骤设计为模板方法,而一些可以改变的细节由子类来实现
- 系统中子类的公共部分需要抽离到父类
- 系统需要通过子类来决定父类中某个步骤是否执行,实现子类对父类的反向控制