设计模式(三)行为型模式
用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。
1.责任链模式(Chain of Responsibility)
为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
其主要优点如下:
1.降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。 2.增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。 3.增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。 4.责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。 5.责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则
主要包含以下角色:
抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
关键代码:
Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。
public class ChainOfResponsibilityPattern { public static void main(String[] args) { //组装责任链 Handler handler1 = new ConcreteHandler1(); Handler handler2 = new ConcreteHandler2(); handler1.setNext(handler2); //提交请求 handler1.handleRequest("two"); } } //抽象处理者角色 abstract class Handler { private Handler next; public void setNext(Handler next) { this.next = next; } public Handler getNext() { return next; } //处理请求的方法 public abstract void handleRequest(String request); } //具体处理者角色1 class ConcreteHandler1 extends Handler { public void handleRequest(String request) { if (request.equals("one")) { System.out.println("具体处理者1负责处理该请求!"); } else { if (getNext() != null) { getNext().handleRequest(request); } else { System.out.println("没有人处理该请求!"); } } } } //具体处理者角色2 class ConcreteHandler2 extends Handler { public void handleRequest(String request) { if (request.equals("two")) { System.out.println("具体处理者2负责处理该请求!"); } else { if (getNext() != null) { getNext().handleRequest(request); } else { System.out.println("没有人处理该请求!"); } } } }
执行结果:
具体处理者2负责处理该请求!
2.命令模式(Command)
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
命令模式包含以下主要角色:
抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
public class CommandPattern { public static void main(String[] args) { Command cmd = new ConcreteCommand(); Invoker ir = new Invoker(cmd); System.out.println("客户访问调用者的call()方法..."); ir.call(); } } //调用者 class Invoker { private Command command; public Invoker(Command command) { this.command = command; } public void setCommand(Command command) { this.command = command; } public void call() { System.out.println("调用者执行命令command..."); command.execute(); } } //抽象命令 interface Command { public abstract void execute(); } //具体命令 class ConcreteCommand implements Command { private Receiver receiver; ConcreteCommand() { receiver = new Receiver(); } public void execute() { receiver.action(); } } //接收者 class Receiver { public void action() { System.out.println("接收者的action()方法被调用..."); } }
执行结果:
客户访问调用者的call()方法...
调用者执行命令command...
接收者的action()方法被调用...
3.迭代器模式(Iterator)
提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器模式是通过将聚合对象的遍历行为分离出来,抽象成迭代器类来实现的,其目的是在不暴露聚合对象的内部结构的情况下,让外部代码透明地访问聚合的内部数据。
迭代器模式主要包含以下角色:
抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
public class IteratorPattern { public static void main(String[] args) { Aggregate ag = new ConcreteAggregate(); ag.add("中山大学"); ag.add("华南理工"); ag.add("韶关学院"); System.out.print("聚合的内容有:"); Iterator it = ag.getIterator(); while (it.hasNext()) { Object ob = it.next(); System.out.print(ob.toString() + "\t"); } Object ob = it.first(); System.out.println("\nFirst:" + ob.toString()); } } //抽象聚合 interface Aggregate { public void add(Object obj); public void remove(Object obj); public Iterator getIterator(); } //具体聚合 class ConcreteAggregate implements Aggregate { private List<Object> list = new ArrayList<Object>(); public void add(Object obj) { list.add(obj); } public void remove(Object obj) { list.remove(obj); } public Iterator getIterator() { return (new ConcreteIterator(list)); } } //抽象迭代器 interface Iterator { Object first(); Object next(); boolean hasNext(); } //具体迭代器 class ConcreteIterator implements Iterator { private List<Object> list = null; private int index = -1; public ConcreteIterator(List<Object> list) { this.list = list; } public boolean hasNext() { if (index < list.size() - 1) { return true; } else { return false; } } public Object first() { index = 0; Object obj = list.get(index); ; return obj; } public Object next() { Object obj = null; if (this.hasNext()) { obj = list.get(++index); } return obj; } }
执行结果:
聚合的内容有:中山大学 华南理工 韶关学院
First:中山大学
4.中介者模式(Mediator)
定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
中介者模式包含以下主要角色:
抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。 具体中介者(Concrete Mediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。
public class MediatorPattern { public static void main(String[] args) { Mediator md = new ConcreteMediator(); Colleague c1, c2; c1 = new ConcreteColleague1(); c2 = new ConcreteColleague2(); md.register(c1); md.register(c2); c1.send(); System.out.println("-------------"); c2.send(); } } //抽象中介者 abstract class Mediator { public abstract void register(Colleague colleague); public abstract void relay(Colleague cl); //转发 } //具体中介者 class ConcreteMediator extends Mediator { private List<Colleague> colleagues = new ArrayList<Colleague>(); public void register(Colleague colleague) { if (!colleagues.contains(colleague)) { colleagues.add(colleague); colleague.setMedium(this); } } public void relay(Colleague cl) { for (Colleague ob : colleagues) { if (!ob.equals(cl)) { ((Colleague) ob).receive(); } } } } //抽象同事类 abstract class Colleague { protected Mediator mediator; public void setMedium(Mediator mediator) { this.mediator = mediator; } public abstract void receive(); public abstract void send(); } //具体同事类 class ConcreteColleague1 extends Colleague { public void receive() { System.out.println("具体同事类1收到请求。"); } public void send() { System.out.println("具体同事类1发出请求。"); mediator.relay(this); //请中介者转发 } } //具体同事类 class ConcreteColleague2 extends Colleague { public void receive() { System.out.println("具体同事类2收到请求。"); } public void send() { System.out.println("具体同事类2发出请求。"); mediator.relay(this); //请中介者转发 } }
执行结果:
具体同事类1发出请求。 具体同事类2收到请求。 ------------- 具体同事类2发出请求。 具体同事类1收到请求。
5.备忘录模式(Memento)
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。
备忘录模式的主要角色如下:
发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
public class MementoPattern { public static void main(String[] args) { Originator or = new Originator(); Caretaker cr = new Caretaker(); or.setState("S0"); System.out.println("初始状态:" + or.getState()); cr.setMemento(or.createMemento()); //保存状态 or.setState("S1"); System.out.println("新的状态:" + or.getState()); or.restoreMemento(cr.getMemento()); //恢复状态 System.out.println("恢复状态:" + or.getState()); } } //备忘录 class Memento { private String state; public Memento(String state) { this.state = state; } public void setState(String state) { this.state = state; } public String getState() { return state; } } //发起人 class Originator { private String state; public void setState(String state) { this.state = state; } public String getState() { return state; } public Memento createMemento() { return new Memento(state); } public void restoreMemento(Memento m) { this.setState(m.getState()); } } //管理者 class Caretaker { private Memento memento; public void setMemento(Memento m) { memento = m; } public Memento getMemento() { return memento; } }
执行结果:
初始状态:S0
新的状态:S1
恢复状态:S0
6.观察者模式(Observer)
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式。
其主要优点如下:
1.降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
2.目标与观察者之间建立了一套触发机制。
观察者模式的主要角色如下:
抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
public class ObserverPattern { public static void main(String[] args) { Subject subject = new ConcreteSubject(); Observer obs1 = new ConcreteObserver1(); Observer obs2 = new ConcreteObserver2(); subject.add(obs1); subject.add(obs2); subject.notifyObserver(); } } //抽象目标 abstract class Subject { protected List<Observer> observers = new ArrayList<Observer>(); //增加观察者方法 public void add(Observer observer) { observers.add(observer); } //删除观察者方法 public void remove(Observer observer) { observers.remove(observer); } public abstract void notifyObserver(); //通知观察者方法 } //具体目标 class ConcreteSubject extends Subject { public void notifyObserver() { System.out.println("具体目标发生改变..."); System.out.println("--------------"); for (Object obs : observers) { ((Observer) obs).response(); } } } //抽象观察者 interface Observer { void response(); //反应 } //具体观察者1 class ConcreteObserver1 implements Observer { public void response() { System.out.println("具体观察者1作出反应!"); } } //具体观察者1 class ConcreteObserver2 implements Observer { public void response() { System.out.println("具体观察者2作出反应!"); } }
执行结果:
具体目标发生改变... -------------- 具体观察者1作出反应! 具体观察者2作出反应!
7.状态模式(State)
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
其主要优点如下:
1.结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
2.将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
3.状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
状态模式包含以下主要角色:
环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
public class StatePatternClient { public static void main(String[] args) { Context context = new Context(); //创建环境 context.Handle(); //处理请求 context.Handle(); context.Handle(); context.Handle(); } } //环境类 class Context { private State state; //定义环境类的初始状态 public Context() { this.state = new ConcreteStateA(); } //设置新状态 public void setState(State state) { this.state = state; } //读取状态 public State getState() { return (state); } //对请求做处理 public void Handle() { state.Handle(this); } } //抽象状态类 abstract class State { public abstract void Handle(Context context); } //具体状态A类 class ConcreteStateA extends State { public void Handle(Context context) { System.out.println("当前状态是 A."); context.setState(new ConcreteStateB()); } } //具体状态B类 class ConcreteStateB extends State { public void Handle(Context context) { System.out.println("当前状态是 B."); context.setState(new ConcreteStateA()); } }
执行结果:
当前状态是 A.
当前状态是 B.
当前状态是 A.
当前状态是 B.
8.模板模式(Template)
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
该模式的主要优点如下:
1.它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
2.它在父类中提取了公共的部分代码,便于代码复用。
3.部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
9.访问者模式(Visitor)
将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离。
其主要优点如下:
1.扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
2.复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
3.灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
4.符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
访问者模式包含以下主要角色:
抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
public class VisitorPattern { public static void main(String[] args) { ObjectStructure os = new ObjectStructure(); os.add(new ConcreteElementA()); os.add(new ConcreteElementB()); Visitor visitor = new ConcreteVisitorA(); os.accept(visitor); System.out.println("------------------------"); visitor = new ConcreteVisitorB(); os.accept(visitor); } } //抽象访问者 interface Visitor { void visit(ConcreteElementA element); void visit(ConcreteElementB element); } //具体访问者A类 class ConcreteVisitorA implements Visitor { public void visit(ConcreteElementA element) { System.out.println("具体访问者A访问-->" + element.operationA()); } public void visit(ConcreteElementB element) { System.out.println("具体访问者A访问-->" + element.operationB()); } } //具体访问者B类 class ConcreteVisitorB implements Visitor { public void visit(ConcreteElementA element) { System.out.println("具体访问者B访问-->" + element.operationA()); } public void visit(ConcreteElementB element) { System.out.println("具体访问者B访问-->" + element.operationB()); } } //抽象元素类 interface Element { void accept(Visitor visitor); } //具体元素A类 class ConcreteElementA implements Element { public void accept(Visitor visitor) { visitor.visit(this); } public String operationA() { return "具体元素A的操作。"; } } //具体元素B类 class ConcreteElementB implements Element { public void accept(Visitor visitor) { visitor.visit(this); } public String operationB() { return "具体元素B的操作。"; } } //对象结构角色 class ObjectStructure { private List<Element> list = new ArrayList<Element>(); public void accept(Visitor visitor) { Iterator<Element> i = list.iterator(); while (i.hasNext()) { ((Element) i.next()).accept(visitor); } } public void add(Element element) { list.add(element); } public void remove(Element element) { list.remove(element); } }
具体访问者A访问-->具体元素A的操作。 具体访问者A访问-->具体元素B的操作。 ------------------------ 具体访问者B访问-->具体元素A的操作。 具体访问者B访问-->具体元素B的操作。
10.解释器模式(Interpreter)
给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。这里提到的文法和句子的概念同编译原理中的描述相同,“文法”指语言的语法规则,而“句子”是语言集中的元素。例如,汉语中的句子有很多,“我是中国人”是其中的一个句子,可以用一棵语法树来直观地描述语言中的句子。
11.策略模式(Strategy)
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
策略模式的主要优点如下:
1.多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if...else 语句、switch...case 语句。 2.策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。 3.策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。 4.策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。 5.策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
策略模式的主要角色如下:
抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
环境(Context)类:持有一个策略类的引用,最终给客户端调用。
public class StrategyPattern { public static void main(String[] args) { Context c = new Context(); Strategy s = new ConcreteStrategyA(); c.setStrategy(s); c.strategyMethod(); System.out.println("-----------------"); s = new ConcreteStrategyB(); c.setStrategy(s); c.strategyMethod(); } } //抽象策略类 interface Strategy { public void strategyMethod(); //策略方法 } //具体策略类A class ConcreteStrategyA implements Strategy { public void strategyMethod() { System.out.println("具体策略A的策略方法被访问!"); } } //具体策略类B class ConcreteStrategyB implements Strategy { public void strategyMethod() { System.out.println("具体策略B的策略方法被访问!"); } } //环境类 class Context { private Strategy strategy; public Strategy getStrategy() { return strategy; } public void setStrategy(Strategy strategy) { this.strategy = strategy; } public void strategyMethod() { strategy.strategyMethod(); } }
执行结果:
具体策略A的策略方法被访问! ----------------- 具体策略B的策略方法被访问!