1 行为模式
行为模式主要涉及到算法和对象间职责的分配。行为类模式使用继承机制在类间分配行为。比如模板方法模式,解释器模式。
行为对象模式使用对象复用而不是继承。
1.1 模板方法模式
1.1.1 意图
Template Method: 定义一个操作中算法的骨架,而将一些步骤延迟到子类。
1.1.2实用性
模板方法应用于下列情况:
• 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
• 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
• 控制子类扩展。
1.1.3结构
1.1.4优缺点
封装不变部分,扩展可变部分。提取公共部分代码,便于维护。行为由父类控制,子类实现。
1.1.5参与者
• A b s t r a c t C l a s s
— 定义抽象的原语操作( primitive operation),具体的子类将重定义它们以实现一个算法
— 实现一个模板方法,定义一个算法的骨架。该模板方法不仅调用原语操作,也调用定义
在A b s t r a c t C l a s s或其他对象中的操作。
• C o n c r e t e C l a s s
— 实现原语操作以完成算法中与特定子类相关的步骤。
1.
AbstractClass
public abstract class AbstractClass {
//
基本方法
protected abstract void doSomething();
//
基本方法
protected abstract void doAnything();
//
模版方法
public void templateMethod(){
/*
*
调用基本方法,完成相关的逻辑
*/
this.doAnything();
this.doSomething();
}
}
2.
ConcreteClass1
public class ConcreteClass1 extends AbstractClass {
//
实现基本方法
protected void doAnything() {
//
业务逻辑处理
}
protected void doSomething() {
//
业务逻辑处理
}
}
1.2 解释器模式
1.2.1 意图
Interpreter: 给定一门语言,定义它文法的一种表示,并定义一个解释器,这个解释器使用该表示可以解释语言中的句子。
1.2.2实用性
当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:
• 该文法简单对于复杂的文法, 文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式, 这样可以节省空间而且还可能节省时间。
• 效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下, 转换器仍可用解释器模式实现, 该模式仍是有用的。
1.2.3结构
1.2.4优缺点
解释器是一个简单的语法分析工具,最显著的优点就是扩展性,修改语法规则只要修改相应的非终结表达式就可以了,若扩展语法,则只要添加非终结符类就可以了。
解释模式的缺点:解释器模式会引起类膨胀,每个语法都要产生一个非终结表达式;采用递归调用方法;效率问题,由于使用了大量的循环和递归,效率是一个问题。
1.2.5参与者
• A b s t r a c t E x p r e s s i o n (抽象表达式)
— 声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。
• Te r m i n a l E x p r e s s i o n (终结符表达式)
— 实现与文法中的终结符相关联的解释操作。
— 一个句子中的每个终结符需要该类的一个实例。
• N o n t e r m i n a l E x p r e s s i o n (非终结符表达式)
— 对文法中的每一条规则R ::= R1R2. . . Rn都需要一个N o n t e r m i n a l E x p r e s s i o n类。
— 为从R1到Rn的每个符号都维护一个A b s t r a c t E x p r e s s i o n类型的实例变量。
— 为文法中的非终结符实现解释( I n t e r p r e t )操作。解释( I n t e r p r e t )一般要递归地调用表示
R1到Rn的那些对象的解释操作。
• C o n t e x t(上下文)
— 包含解释器之外的一些全局信息。
• C l i e n t(客户)
— 构建(或被给定) 表示该文法定义的语言中一个特定的句子的抽象语法树。该抽象语法树由N o n t e r m i n a l E x p r e s s i o n和Te r m i n a l E x p r e s s i o n的实例装配而成。
— 调用解释操作。
1.
Expression
public abstract class Expression {
//
每个表达式必须有一个解析任务
public abstract Object interpreter(Context ctx);
}
2.
NonterminalExpression
public class NonterminalExpression extends Expression {
//
每个非终结符表达式都会对其他表达式产生依赖
public NonterminalExpression(Expression... expression){
}
public Object interpreter(Context ctx) {
//
进行文法处理
return null;
}
}
3.
TerminalExpression
public class TerminalExpression extends Expression {
//
通常终结符表达式只有一个,但是有多个对象
public Object interpreter(Context ctx) {
return null;
}
}
4.
Context
public class Context {
}
5.
Client
public class Client {
public static void main(String[] args) {
Context ctx = new Context();
//
通常定一个语法容器,容纳一个具体的表达式,通常为ListArray,LinkedList,Stack等类型
Stack<Expression> stack = null;
/*
for(;;){
//
进行语法判断,并产生递归调用
}
*/
//
产生一个完整的语法树,由各各个具体的语法分析进行解析
Expression exp = stack.pop();
//
具体元素进入场景
exp.interpreter(ctx);
}
}
1.3 责任链模式
1.3.1 意图
Chain Of Resopnsibility: 使得多个对象都有机会处理请求,从而避免请求的发送者与接收者之间的耦合关系。将这些对象形成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
1.3.2实用性
在以下条件下使用Responsibility链:
• 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
• 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
• 可处理一个请求的对象集合应被动态指定。
1.3.3结构
1.3.4优缺点
责任链模式将请求与处理分开。请求者可以不用知道是谁处理的,处理者可以不用知道请求者,两者解耦,提高系统的灵活性。
缺点则是:一性能问题,链比较长的时候,性能是个大问题;二调试不方便,由于使用了类似递归的方法。
1.3.5参与者
• H a n d l e r
— 定义一个处理请求的接口。
— (可选) 实现后继链。
• C o n c r e t e H a n d l e r
— 处理它所负责的请求。
— 可访问它的后继者。
— 如果可处理该请求,就处理之;否则将该请求转发给它的后继者。
• C l i e n t
— 向链上的具体处理者( C o n c r e t e H a n d l e r )对象提交请求。
1.
Handler
public abstract class Handler {
private Handler nextHandler;
//
每个处理者都必须对请求做出处理
public final Response handlerMessage(Request request){
Response response = null;
//
判断是否是自己的处理级别
if(this.getHandlerLevel().equals(request.getRequestLevel())){
response = this.echo(request);
}else{ //
不属于自己的处理级别
//
判断是否有下一个处理者
if(this.nextHandler != null){
response = this.nextHandler.handlerMessage(request);
}else{
//
没有适当的处理者,业务自行处理
}
}
return response;
}
//
设置下一个处理者是谁
public void setNext(Handler _handler){
this.nextHandler = _handler;
}
//
每个处理者都有一个处理级别
protected abstract Level getHandlerLevel();
//
每个处理者都必须实现处理任务
protected abstract Response echo(Request request);
}
2.
ConcreteHandler1
public class ConcreteHandler1 extends Handler {
//
定义自己的处理逻辑
protected Response echo(Request request) {
//
完成处理逻辑
return null;
}
//
设置自己的处理级别
protected Level getHandlerLevel() {
//
设置自己的处理级别
return null;
}
}
3.
Level
public class Level {
//
定义一个请求和处理等级
}
4.
Request
public class Request {
//
请求的等级
public Level getRequestLevel(){
return null;
}
}
5.
Client
public class Client {
public static void main(String[] args) {
//
声明出所有的处理节点
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
Handler handler3 = new ConcreteHandler3();
//
设置链中的阶段顺序,1-->2-->3
handler1.setNext(handler2);
handler2.setNext(handler3);
//
提交请求,返回结果
Response response = handler1.handlerMessage(new Request());
}
}
1.4 迭代器模式
1.4.1 意图
Iterator: 提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露改对象的内部表示。
1.4.2实用性
迭代器模式可用来:
• 访问一个聚合对象的内容而无需暴露它的内部表示。
• 支持对聚合对象的多种遍历。
• 为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。
1.4.3结构
1.4.4参与者
• I t e r a t o r(迭代器)
— 迭代器定义访问和遍历元素的接口。
• C o n c r e t e I t e r a t o r(具体迭代器)
— 具体迭代器实现迭代器接口。
— 对该聚合遍历时跟踪当前位置。
• A g g r e g a t e(聚合)
— 聚合定义创建相应迭代器对象的接口。
• C o n c r e t e A g g r e g a t e(具体聚合)
— 具体聚合实现创建相应迭代器的接口,该操作返回C o n c r e t e I t e r a t o r的一个适当的实例。
1.
Iterator
public interface Iterator {
//
遍历到下一个元素
public Object next();
//
是否已经遍历到尾部
public boolean hasNext();
//
删除当前指向的元素
public boolean remove();
}
2.
ConcreteIterator
public class ConcreteIterator implements Iterator {
private Vector vector = new Vector();
//
定义当前游标
public int cursor = 0;
@SuppressWarnings("unchecked")
public ConcreteIterator(Vector _vector){
this.vector = _vector;
}
//
判断是否到达尾部
public boolean hasNext() {
if(this.cursor == this.vector.size()){
return false;
}else{
return true;
}
}
//
返回下一个元素
public Object next() {
Object result = null;
if(this.hasNext()){
result = this.vector.get(this.cursor++);
}else{
result = null;
}
return result;
}
//
删除当前元素
public boolean remove() {
this.vector.remove(this.cursor);
return true;
}
}
3.
Aggregate
public interface Aggregate {
//
是容器必然有元素的增加
public void add(Object object);
//
减少元素
public void remove(Object object);
//
由迭代器来遍历所有的元素
public Iterator iterator();
}
4.
ConcreteAggregate
public class ConcreteAggregate implements Aggregate {
//
容纳对象的容器
private Vector vector = new Vector();
//
增加一个元素
public void add(Object object) {
this.vector.add(object);
}
//
返回迭代器对象
public Iterator iterator() {
return new ConcreteIterator(this.vector);
}
//
删除一个元素
public void remove(Object object) {
this.remove(object);
}
}
5.
Client
public class Client {
public static void main(String[] args) {
//
声明出容器
Aggregate agg = new ConcreteAggregate();
//
产生对象数据放进去
agg.add("abc");
agg.add("aaa");
agg.add("1234");
//
遍历一下
Iterator iterator = agg.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
1.5 命令模式
1.5.1 意图
Command: 将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化。
1.5.2实用性
当你有如下需求时,可使用C o m m a n d模式:
• 像上面讨论的Menu I t e m对象那样,抽象出待执行的动作以参数化某对象。你可用过程语言中的回调(c a l l b a c k)函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。C o m m a n d模式是回调机制的一个面向对象的替代品。
• 在不同的时刻指定、排列和执行请求。一个C o m m a n d对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程并在那儿实现该请求。
• 支持取消操作。C o m m a n d的E x c u t e操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。C o m m a n d接口必须添加一个U n e x e c u t e操作,该操作取消上一次E x e c u t e调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用U n e x e c u t e和E x e c u t e来实现重数不限的“取消”和“重做”。
• 支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。在C o m m a n d接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用E x e c u t e操作重新执行它们。
• 用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务( t r a n s a c t i o n )的信息系统中很常见。一个事务封装了对数据的一组变动。C o m m a n d模式提供了对事务进行建模的方法。C o m m a n d有一个公共的接口,使得你可以用同一种方式调用所有的事务。同时使用该模式也易于添加新事务以扩展系统。
1.5.3结构
1.5.4优缺点
命令模式将调用者与接收者角色分离解耦;增加了可扩展性;缺点则是:如果命令过多,则类会膨胀的特别大。
1.5.5参与者
• C o m m a n d
— 声明执行操作的接口。
• C o n c r e t e C o m m a n d
— 将一个接收者对象绑定于一个动作。
— 调用接收者相应的操作,以实现E x e c u t e。
• C l i e n t
— 创建一个具体命令对象并设定它的接收者。
• Invoker
— 要求该命令执行这个请求。
• R e c e i v e r
— 知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。
1.
Command
public abstract class Command {
//
定义一个子类的全局共享变量
protected final Receiver receiver;
//
实现类必须定义一个接收者
public Command(Receiver _receiver){
this.receiver = _receiver;
}
//
每个命令类都必须有一个执行命令的方法
public abstract void execute();
}
2.
ConcreteCommand1
public class ConcreteCommand1 extends Command {
//
声明自己的默认接收者
public ConcreteCommand1(){
super(new ConcreteReciver1());
}
//
设置新的接收者
public ConcreteCommand1(Receiver _receiver){
super(_receiver);
}
//
每个具体的命令都必须实现一个命令
public void execute() {
//
业务处理
super.receiver.doSomething();
}
}
3.
Receiver
public abstract class Receiver {
//
抽象接收者,定义每个接收者都必须完成的业务
public abstract void doSomething();
}
4.
ConcreteReciver1
public class ConcreteReciver1 extends Receiver{
//
每个接受者都必须处理一定的业务逻辑
public void doSomething(){
}
}
5.
Invoker
public class Invoker {
private Command command;
//
受气包,接受命令
public void setCommand(Command _command){
this.command = _command;
}
//
执行命令
public void action(){
this.command.execute();
}
}
6.
Client
public class Client {
public static void main(String[] args) {
//
首先声明出调用者Invoker
Invoker invoker = new Invoker();
//
定义一个发送给接收者的命令
Command command = new ConcreteCommand1();
//
把命令交给调用者去执行
invoker.setCommand(command);
invoker.action();
}
}
1.6 状态模式
1.6.1 意图
State: 允许一个对象在其内部状态改变时改变它的行为。对象开起来似乎修改了它的类。
1.6.2实用性
在下面的两种情况下均可使用S t a t e模式:
• 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
• 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常, 有多个操作包含这一相同的条件结构。S t a t e模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。
1.6.3结构
1.6.4优缺点
状态模式的优点就是结构清晰,封装性好,但有一个缺点,就是子类会太多,类膨胀。
1.6.5参与者
• C o n t e x t
— 定义客户感兴趣的接口。
— 维护一个C o n c r e t e S t a t e子类的实例,这个实例定义当前状态。
• S t a t e
— 定义一个接口以封装与C o n t e x t的一个特定状态相关的行为。
• ConcreteState subclasses
— 每一子类实现一个与C o n t e x t的一个状态相关的行为。
1.
Context
public class Context {
//
定义状态
public final static State STATE1 = new ConcreteState1();
public final static State STATE2 = new ConcreteState2();
//
当前状态
private State CurrentState;
//
获得当前状态
public State getCurrentState() {
return CurrentState;
}
//
设置当前状态
public void setCurrentState(State currentState) {
this.CurrentState = currentState;
//
切换状态
this.CurrentState.setContext(this);
}
//
行为委托
public void handle1(){
this.CurrentState.handle1();
}
public void handle2(){
this.CurrentState.handle2();
}
}
2.
State
public abstract class State {
//
定义一个环境角色,提供子类访问
protected Context context;
//
设置环境角色
public void setContext(Context _context){
this.context = _context;
}
//
行为1
public abstract void handle1();
//
行为2
public abstract void handle2();
}
3.
ConcreteState1
public class ConcreteState1 extends State {
@Override
public void handle1() {
//
本状态下必须处理的逻辑
}
@Override
public void handle2() {
//
设置当前状态为stat2
super.context.setCurrentState(Context.STATE2);
//
过渡到state2状态,由Context实现
super.context.handle2();
}
}
4.
ConcreteState2
public class ConcreteState2 extends State {
@Override
public void handle1() {
//
设置当前状态为stat1
super.context.setCurrentState(Context.STATE1);
//
过渡到state1状态,由Context实现
super.context.handle1();
}
@Override
public void handle2() {
//
本状态下必须处理的逻辑
}
}
5.
Client
public class Client {
public static void main(String[] args) {
//
定义环境角色
Context context = new Context();
//
初始化状态
context.setCurrentState(new ConcreteState1());
//
行为执行
context.handle1();
context.handle2();
}
}
1.7 策略模式
1.7.1 意图
Strategy: 定义一系列的算法,把他们一个个都封装起来,并且使他们可以相互替换。
1.7.2实用性
当存在以下情况时使用S t r a t e g y模式
• 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
• 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时[ H O 8 7 ] ,可以使用策略模式。
• 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
• 一个类定义了多种行为, 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的S t r a t e g y类中以代替这些条件语句。
1.7.3结构
1.7.4优缺点
策略模式的优点:算法可以自由切换;避免使用多重条件判断;扩展性良好。缺点则是策略类数量增多
1.7.5参与者
• S t r a t e g y(策略)
— 定义所有支持的算法的公共接口。C o n t e x t使用这个接口来调用某C o n c r e t e S t r a t e g y定
义的算法。
• C o n c r e t e S t r a t e g y(具体策略)
— 以S t r a t e g y接口实现某具体算法。
• C o n t e x t(上下文)
— 用一个C o n c r e t e S t r a t e g y对象来配置。
— 维护一个对S t r a t e g y对象的引用。
— 可定义一个接口来让S t a t e g y访问它的数据。
1.
Context
public class Context {
//
抽象策略
private Strategy strategy = null;
//
构造函数设置具体策略
public Context(Strategy _strategy){
this.strategy = _strategy;
}
//
封装后的策略方法
public void doAnythinig(){
this.strategy.doSomething();
}
}
2.
Strategy
public interface Strategy {
//
策略模式的运算法则
public void doSomething();
}
3.
ConcreteStrategy1
public class ConcreteStrategy1 implements Strategy {
public void doSomething() {
System.out.println("
具体策略1的运算法则");
}
}
4.
ConcreteStrategy2
public class ConcreteStrategy2 implements Strategy {
public void doSomething() {
System.out.println("
具体策略2的运算法则");
}
}
5.
Client
public class Client {
public static void main(String[] args) {
//
声明出一个具体的策略
Strategy strategy = new ConcreteStrategy1();
//
声明出上下文对象
Context context = new Context(strategy);
//
执行封装后的方法
context.doAnythinig();
}
}
1.8 访问者模式
1.8.1 意图
Visitor: 表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
1.8.2实用性
在下列情况下使用Vi s i t o r模式:
• 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
• 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Vi s i t o r使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Vi s i t o r模式让每个应用仅包含需要用到的操作。
• 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
1.8.3结构
1.8.4优缺点
访问者模式的优点:符合单一职责原则,优秀的拓展性,灵活性很高;缺点则是具体元素对访问者公布细节;具体元素变更比较困难。
1.8.5参与者
• Vi s i t o r(访问者)
— 为该对象结构中C o n c r e t e E l e m e n t的每一个类声明一个Vi s i t操作。该操作的名字和特征标识了发送Vi s i t请求给该访问者的那个类。这使得访问者可以确定正被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。
• C o n c r e t e Vi s i t o r(具体访问者)
— 实现每个由Vi s i t o r声明的操作。每个操作实现本算法的一部分,而该算法片断乃是对应于结构中对象的类。C o n c r e t e Vi s i t o r为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该结构的过程中累积结果。
• E l e m e n t(元素)
— 定义一个A c c e p t操作,它以一个访问者为参数。
• C o n c r e t e E l e m e n t(具体元素)
— 实现A c c e p t操作,该操作以一个访问者为参数。
• O b j e c t S t r u c t u r e(对象结构)
— 能枚举它的元素。
— 可以提供一个高层的接口以允许该访问者访问它的元素。
— 可以是一个复合或是一个集合,如一表或一个无序集合。
1.
IVisitor
public interface IVisitor {
//
可以访问哪些对象
public void visit(ConcreteElement1 el1);
public void visit(ConcreteElement2 el2);
}
2.
Visitor
public class Visitor implements IVisitor {
//
访问el1元素
public void visit(ConcreteElement1 el1) {
el1.doSomething();
}
//
访问el2元素
public void visit(ConcreteElement2 el2) {
el2.doSomething();
}
}
3.
Element
public abstract class Element {
//
定义业务逻辑
public abstract void doSomething();
//
允许谁来访问
public abstract void accept(IVisitor visitor);
}
4.
ConcreteElement1
public class ConcreteElement1 extends Element{
//
完善业务逻辑
public void doSomething(){
//
业务处理
}
//
允许那个访问者访问
public void accept(IVisitor visitor){
visitor.visit(this);
}
}
5.
ConcreteElement2
public class ConcreteElement2 extends Element{
//
完善业务逻辑
public void doSomething(){
//
业务处理
}
//
允许那个访问者访问
public void accept(IVisitor visitor){
visitor.visit(this);
}
}
6.
ObjectStruture
public class ObjectStruture {
//
对象生成器,这里通过一个工厂方法模式模拟
public static Element createElement(){
Random rand = new Random();
if(rand.nextInt(100) > 50){
return new ConcreteElement1();
}else{
return new ConcreteElement2();
}
}
}
7.
Client
public class Client {
public static void main(String[] args) {
for(int i=0;i<10;i++){
//
获得元素对象
Element el = ObjectStruture.createElement();
//
接受访问者访问
el.accept(new Visitor());
}
}
}
1.9 中介者模式
1.9.1 意图
Mediator: 用一个中介对象来封装一系列的对象的交互。中介者使各个对象不需要显示的相互引用,从而使其松散。
1.9.2实用性
在下列情况下使用中介者模式:
• 一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
• 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
• 想定制一个分布在多个类中的行为,而又不想生成太多的子类。
1.9.3结构
1.9.4优缺点
中介者模式的优点就是减少类间的依赖,把原有的一对多的依赖变成一对一的依赖,同事类只依赖中介者,减少了依赖,降低了类间的耦合性。
缺点:中介者模式的缺点就是中介者会膨胀的很大,而且逻辑复杂。
1.9.5参与者
• M e d i a t o r
— 中介者定义一个接口用于与各同事对象通信。
• C o n c r e t e M e d i a t o r
— 具体中介者通过协调各同事对象实现协作行为。
— 了解并维护它的各个同事。
• Colleague
— 每一个同事类都知道它的中介者对象。
— 每一个同事对象在需与其他的同事通信的时候,与它的中介者通信。
1.
Mediator
//
中介者
public abstract class Mediator {
//
定义同事类
protected ConcreteColleague1 c1;
protected ConcreteColleague2 c2;
//
通过getter/setter方法吧同事类注入进来
public ConcreteColleague1 getC1() {
return c1;
}
public void setC1(ConcreteColleague1 c1) {
this.c1 = c1;
}
public ConcreteColleague2 getC2() {
return c2;
}
public void setC2(ConcreteColleague2 c2) {
this.c2 = c2;
}
//
中介者模式的业务逻辑
public abstract void doSomething1();
public abstract void doSomething2();
}
2.
ConcreteMediator
public class ConcreteMediator extends Mediator {
@Override
public void doSomething1() {
//
调用同事类的方法,只要是public方法都可以调用
super.c1.selfMethod1();
super.c2.selfMethod2();
}
public void doSomething2() {
super.c1.selfMethod1();
super.c2.selfMethod2();
}
}
3.
Colleague
/*
*
同事类
*/
public abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator _mediator){
this.mediator = _mediator;
}
}
4.
ConcreteColleague1
public class ConcreteColleague1 extends Colleague {
//
通过构造函数传递中介者
public ConcreteColleague1(Mediator _mediator){
super(_mediator);
}
//
自有方法 self-method
public void selfMethod1(){
//
处理自己的业务逻辑
}
//
依赖方法 dep-method
public void depMethod1(){
//
处理自己的业务逻辑
//
自己不能处理的业务逻辑,委托给中介者处理
super.mediator.doSomething1();
}
}
1.10 备忘录模式
1.10.1 意图
Memento: 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将对象恢复到原先保存的状态。
1.10.2 实用性
在以下情况下使用备忘录模式:
• 必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态。
• 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
1.10.3 结构
1.10.4 参与者
• M e m e n t o
— 备忘录存储原发器对象的内部状态。原发器根据需要决定备忘录存储原发器的哪些内部状态。
— 防止原发器以外的其他对象访问备忘录。备忘录实际上有两个接口,管理者
( c a r e t a k e r )只能看到备忘录的窄接口—它只能将备忘录传递给其他对象。相反, 原发器能够看到一个宽接口, 允许它访问返回到先前状态所需的所有数据。理想的情况是只允许生成本备忘录的那个原发器访问本备忘录的内部状态。
• O r i g i n a t o r
— 原发器创建一个备忘录,用以记录当前时刻它的内部状态。
— 使用备忘录恢复内部状态.。
• C a r e t a k e r
— 负责保存好备忘录。
— 不能对备忘录的内容进行操作或检查。
1.
Memento
public class Memento {
//
发起人的内部状态
private String state = "";
//
构造函数传递参数
public Memento(String _state){
this.state = _state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
2.
Originator
public class Originator {
//
内部状态
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//
创建一个备忘录
public Memento createMemento(){
return new Memento(this.state);
}
//
恢复一个备忘录
public void restoreMemento(Memento _memento){
this.setState(_memento.getState());
}
}
3.
Caretaker
public class Caretaker {
//
容纳备忘录的容器
private HashMap<String,Memento> memMap = new HashMap<String,Memento>();
public Memento getMemento(String idx) {
return memMap.get(idx);
}
public void setMemento(String idx,Memento memento) {
this.memMap.put(idx, memento);
}
}
4.
Client
public class Client {
public static void main(String[] args) {
//
定义出发起人
Originator originator = new Originator();
//
定义出备忘录管理员
Caretaker caretaker = new Caretaker();
//
创建两个备忘录
caretaker.setMemento("001",originator.createMemento());
caretaker.setMemento("002",originator.createMemento());
//
恢复一个指定标记的备忘录
originator.restoreMemento(caretaker.getMemento("001"));
}
}
1.11 观察者模式
1.11.1 意图
Observer: 定义对象间的一种一对多的依赖关系,当一个对象的状态改变时,所有依赖它的对象都得到通知并自动更新。
1.11.2 实用性
在以下任一情况下可以使用观察者模式:
• 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
• 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待变。
• 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
1.11.3 结构
1.11.4 优缺点
观察者和被观察者之间是抽象耦合的,观察者和被观察者都是很容易扩展。建立了一套触发机制。缺点:运行效率,java中消息的通知默认是顺序执行的,一个观察者卡壳,会影响整体,一般建议采用异步。
1.11.5 参与者
• S u b j e c t(目标)
— 目标知道它的观察者。可以有任意多个观察者观察同一个目标。
— 提供注册和删除观察者对象的接口。
• O b s e r v e r(观察者)
— 为那些在目标发生改变时需获得通知的对象定义一个更新接口。
• C o n c r e t e S u b j e c t(具体目标)
— 将有关状态存入各C o n c r e t e O b s e r v e r对象。
— 当它的状态发生改变时, 向它的各个观察者发出通知。
• C o n c r e t e O b s e r v e r(具体观察者)
— 维护一个指向C o n c r e t e S u b j e c t对象的引用。
— 存储有关状态,这些状态应与目标的状态保持一致。
— 实现O b s e r v e r的更新接口以使自身状态与目标的状态保持一致。
1.
Subject
public abstract class Subject {
//
定一个一个观察者数组
private Vector<Observer> obsVector = new Vector<Observer>();
//
增加一个观察者
public void addObserver(Observer o){
this.obsVector.add(o);
}
//
删除一个观察者
public void delObserver(Observer o){
this.obsVector.remove(o);
}
//
通知所有观察者
public void notifyObserver(){
for(Observer o:this.obsVector){
o.update();
}
}
}
2.
ConcreteSubject
public class ConcreteSubject extends Subject {
//
具体的业务
public void doSomething(){
/*
* do something
*/
super.notifyObserver();
}
}
3.
Observer
public interface Observer {
//
更新方法
public void update();
}
4.
ConcreteObserver
public class ConcreteObserver implements Observer {
//
实现更新方法
public void update() {
System.out.println("
接收到信息,并进行处理!");
}
}
5.
Client
public class Client {
public static void main(String[] args) {
//
创建一个被观察者
ConcreteSubject subject = new ConcreteSubject();
//
定义一个观察则
Observer obs= new ConcreteObserver();
//
观察者观察被被观察则
subject.addObserver(obs);
//
观察者开始活动了
subject.doSomething();
}
}
1.12 行为模式的讨论
封装变化是很多行为模式的主题。当一个程序的某个方面的特征经常发生变化时,这些模式就可以定义一个封装这个方面的对象。比如
• 一个S t r a t e g y对象封装一个算法
• 一个S t a t e对象封装一个与状态相关的行为
• 一个M e d i a t o r对象封装对象间的协议
• 一个I t e r a t o r对象封装访问和遍历一个聚集对象中的各个构件的方法
这些模式描述了程序中很可能会改变的方面。
一些模式引入总是被用作参数的对象。
1.12.1 命令模式与策略模式
命令模式和策略模式的类图很相似,单二者还是有区别的;策略模式意图封装算法,它认为算法已经是一个完整的,不可分割的原子业务,其意图是让算法独立,并且可以相互替换;而命令模式则是对动作的解耦,把一个动作的执行分为执行动作、执行命令,让两者相互独立而不相互影响。两者区别如下
l 关注点不同
策略模式关注的是算法替换问题;命令模式关注解耦问题,如何让请求者和执行者解耦是它首要解决的,解耦要求把请求的内容封装成一个命令,由接受者执行。
1.12.2 策略模式与状态模式
状态模式和策略模式非常相似,两者类图如下:
两个类图非常相似,都是通过Context类封装一个具体的行为,都提供了一个封装的方法。单两者还是有区别的:策略模式封装的是不同的算法,算法之间没有交互,以达到随时切换的目的;状态模式封装的是不同的状态,以达到随时切换。
l 环境角色的职责不同
两者都有一个context环境角色的类,但两者的区别很大,策略模式的环境角色只是委托作用,负责算法的替换;状态模式不仅是委托,还具有登记状态变化的功能。
l 解决问题的重点不同
策略模式旨在解决内部算法如何改变的问题;而状态模式旨在解决内在状态改变而引起行为改变的问题,它的出发点是事物的状态,封装状态而暴露行为。
1.12.3 观察者模式与责任链模式
观察者模式和责任链模式比较:链中的消息对象不同,责任链模式基本不改变消息对象的结构,虽然每个节点都参与消费。观察者模式时允许的。上下节点关系不同,消息的渠道不同