设计模式(四) 行为型模式

来源:互联网 发布:淘宝卖家利润 编辑:程序博客网 时间:2024/05/16 18:57

11种行为型模式包括:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。先来张图,看看这11中模式的关系:

第一类:通过父类与子类的关系进行实现。第二类:两个类之间。第三类:类的状态。第四类:通过中间类


13、策略模式(strategy)

策略模式是指,定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数,关系图如下:


图中ICalculator提供同意的方法,
AbstractCalculator是辅助类,提供辅助方法,接下来,依次实现下每个类:

代码如下:

    public interface ICalculator {          public int calculate(String exp);      }      public abstract class AbstractCalculator {                    public int[] split(String exp,String opt){              String array[] = exp.split(opt);              int arrayInt[] = new int[2];              arrayInt[0] = Integer.parseInt(array[0]);              arrayInt[1] = Integer.parseInt(array[1]);              return arrayInt;          }      }      public class Plus extends AbstractCalculator implements ICalculator {                @Override          public int calculate(String exp) {              int arrayInt[] = split(exp,"\\+");              return arrayInt[0]+arrayInt[1];          }      }      public class Minus extends AbstractCalculator implements ICalculator {                @Override          public int calculate(String exp) {              int arrayInt[] = split(exp,"-");              return arrayInt[0]-arrayInt[1];          }            }      public class Multiply extends AbstractCalculator implements ICalculator {                @Override          public int calculate(String exp) {              int arrayInt[] = split(exp,"\\*");              return arrayInt[0]*arrayInt[1];          }      }          public class StrategyTest {                public static void main(String[] args) {              String exp = "2+8";              ICalculator cal = new Plus();              int result = cal.calculate(exp);              System.out.println(result);          }      }  

输出:10

策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。

补充

策略模式与工厂方法模式:

策略模式和简单工厂模式看起来非常相似,都是通过多态来实现不同子类的选取,这种思想应该是从程序的整体来看得出的。如果从使用这两种模式的角度来看的话,我们会发现在简单工厂模式中我们只需要传递相应的条件就能得到想要的一个对象,然后通过这个对象实现算法的操作。而策略模式,使用时必须首先创建一个想使用的类对象,然后通过该对象调用不同的算法。在简单工厂模式中实现了通过条件选取一个类去实例化对象,策略模式则将选取相应对象的工作交给模式的使用者,它本身不去做选取工作。

其实两者差别很微妙,简单工厂方法模式是直接创建具体的对象并用该对象去执行相应的动作,而策略模式将这个操作给了客户端,没有创建具体的对象,实现的代码的进一步封装,客户端代码并不需要知道具体的实现过程。

14、模板方法模式(Template Method)

模板方法模式就是指,定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模版方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。具体来说,一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用,先看个关系图:


就是在AbstractCalculator类中定义一个主方法calculate,calculate()调用spilt()等,Plus和Minus分别继承AbstractCalculator类,通过对AbstractCalculator的调用实现对子类的调用,看下面的例子:

    public abstract class AbstractCalculator {                    /*主方法,实现对本类其它方法的调用*/          public final int calculate(String exp,String opt){              int array[] = split(exp,opt);              return calculate(array[0],array[1]);          }                    /*被子类重写的方法*/          abstract public int calculate(int num1,int num2);                    public int[] split(String exp,String opt){              String array[] = exp.split(opt);              int arrayInt[] = new int[2];              arrayInt[0] = Integer.parseInt(array[0]);              arrayInt[1] = Integer.parseInt(array[1]);              return arrayInt;          }      }      public class Plus extends AbstractCalculator {                @Override          public int calculate(int num1,int num2) {              return num1 + num2;          }      }      public class StrategyTest {            public static void main(String[] args) {              String exp = "8+8";              AbstractCalculator cal = new Plus();              int result = cal.calculate(exp, "\\+");              System.out.println(result);          }      }  

输出:16

补充

模板方法模式与策略模式:

策略模式允许外界使用其接口,每个子类都可以视为独立的,整个的算法。模板方法模式,父类可以视作模板,子类只是在此基础上润色,且只能实现父类预留的抽象接口,算法流程相对来说固定。

15、观察者模式(Observer)

包括这个模式在内的接下来的四个模式,都是类和类之间的关系,不涉及到继承,参考本文开头的图。

观察者模式是指,定义对象间的一种一对多的依赖关系 ,当一个对象的状态发生改变时 , 所有依赖于它的对象都得到通知并被自动更新。观察者模式很好理解,类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。先来看看关系图:


MySubject类就是我们的主对象,Observer1和Observer2是依赖于MySubject的对象,当MySubject变化时,Observer1和Observer2必然变化。Observer是抽象的观察者,Observer1和Observer2是具体的观察者。AbstractSubject类中定义着需要监控的对象列表,可以对其进行修改:增加或删除被监控对象,且当MySubject变化时,负责通知在列表内存在的对象。AbstractSubject是抽象的被观察者,MySubject是具体的观察者。实现代码:

    public interface Observer {          public void update();      }      public class Observer1 implements Observer {                @Override          public void update() {              System.out.println("observer1 has received!");          }      }      public class Observer2 implements Observer {                @Override          public void update() {              System.out.println("observer2 has received!");          }            }      public interface Subject {                    /*增加观察者*/          public void add(Observer observer);                    /*删除观察者*/          public void del(Observer observer);                    /*通知所有的观察者*/          public void notifyObservers();                    /*自身的操作*/          public void operation();      }      public abstract class AbstractSubject implements Subject {                private Vector<Observer> vector = new Vector<Observer>();          @Override          public void add(Observer observer) {              vector.add(observer);          }                @Override          public void del(Observer observer) {              vector.remove(observer);          }                @Override          public void notifyObservers() {              Enumeration<Observer> enumo = vector.elements();              while(enumo.hasMoreElements()){                  enumo.nextElement().update();              }          }      }      public class MySubject extends AbstractSubject {                @Override          public void operation() {              System.out.println("update self!");              notifyObservers();          }            }      public class ObserverTest {        public static void main(String[] args) {          Subject sub = new MySubject();          sub.add(new Observer1());          sub.add(new Observer2());                    sub.operation();      }    }

输出:

update self!
observer1 has received!
observer2 has received!


16、迭代器模式(Iterator)

迭代器模式,指的是提供一种方法顺序访问一个聚合对象中各个元素 , 而又不需暴露该对象的内部表示。该模式集合类中非常常见,包含两层含义:一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问。我们看下关系图:

 

这个思路和我们常用的一模一样,MyCollection中定义了集合的一些操作,MyIterator中定义了一系列迭代操作,且持有Collection实例,我们来看看实现代码:

    public interface Collection {                    public Iterator iterator();                    /*取得集合元素*/          public Object get(int i);                    /*取得集合大小*/          public int size();      }      public interface Iterator {          //前移          public Object previous();                    //后移          public Object next();          public boolean hasNext();                    //取得第一个元素          public Object first();      }      public class MyCollection implements Collection {                public String string[] = {"A","B","C","D","E"};          @Override          public Iterator iterator() {              return new MyIterator(this);          }                @Override          public Object get(int i) {              return string[i];          }                @Override          public int size() {              return string.length;          }      }      public class MyIterator implements Iterator {                private Collection collection;          private int pos = -1;                    public MyIterator(Collection collection){              this.collection = collection;          }                    @Override          public Object previous() {              if(pos > 0){                  pos--;              }              return collection.get(pos);          }                @Override          public Object next() {              if(pos<collection.size()-1){                  pos++;              }              return collection.get(pos);          }                @Override          public boolean hasNext() {              if(pos<collection.size()-1){                  return true;              }else{                  return false;              }          }                @Override          public Object first() {              pos = 0;              return collection.get(pos);          }            }     public class Test {                public static void main(String[] args) {              Collection collection = new MyCollection();              Iterator it = collection.iterator();                            while(it.hasNext()){                  System.out.println(it.next());              }          }      }   

输出:A B C D E

值得一提的是,在jdk的集合类(如java.util.AbstractList)中,具体的迭代器持有集合对象的方式并不是像上面示例中的那样:  return new MyIterator(this);   

而是将迭代器的具体实现类作为集合类的内部类。

17、责任链模式(Chain of Responsibility)
责任链模式是指,使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。其中发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。先看看关系图:


Abstracthandler类提供了get和set方法,方便MyHandle类设置和修改引用对象,MyHandle类是核心,实例化后生成一系列相互持有的对象,构成一条链。代码如下:

    public interface Handler {          public void operator();      }      public abstract class AbstractHandler {                    private Handler handler;                public Handler getHandler() {              return handler;          }                public void setHandler(Handler handler) {              this.handler = handler;          }                }      public class MyHandler extends AbstractHandler implements Handler {                private String name;                public MyHandler(String name) {              this.name = name;          }                @Override          public void operator() {              System.out.println(name+"deal!");              if(getHandler()!=null){                  getHandler().operator();              }          }      }      public class Test {                public static void main(String[] args) {              MyHandler h1 = new MyHandler("h1");              MyHandler h2 = new MyHandler("h2");              MyHandler h3 = new MyHandler("h3");                    h1.setHandler(h2);              h2.setHandler(h3);                    h1.operator();          }      }  

输出:

h1deal!
h2deal!
h3deal!

值得一提的是,链接上的请求可以是一条链,可以是一个树,还可以是一个环,模式本身不约束这个,需要我们自己去实现,同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。

拦截器Filter就是使用了责任链模式,创建一个Filter除了要在web.xml文件中做相应配置外,还需要实现javax.servlet.Filter接口。

public class TestFilter implements Filter{    public void doFilter(ServletRequest request, ServletResponse response,            FilterChain chain) throws IOException, ServletException {                chain.doFilter(request, response);    }    public void destroy() {    }    public void init(FilterConfig filterConfig) throws ServletException {    }}

Filter对象并不直接持有下一链的对象,而是由统一的ApplicationFilterConfig数组管理。

 18、命令模式(Command)

命令模式是指,将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。我们看看关系图:


Invoker是调用者(司令员),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象,看实现代码: 

    public interface Command {          public void exe();      }      public class MyCommand implements Command {                private Receiver receiver;                    public MyCommand(Receiver receiver) {              this.receiver = receiver;          }                @Override          public void exe() {              receiver.action();          }      }      public class Receiver {          public void action(){              System.out.println("command received!");          }      }      public class Invoker {                    private Command command;                    public Invoker(Command command) {              this.command = command;          }                public void action(){              command.exe();          }      }      public class Test {                public static void main(String[] args) {              Receiver receiver = new Receiver();              Command cmd = new MyCommand(receiver);              Invoker invoker = new Invoker(cmd);              invoker.action();          }      } 

输出:command received!

命令模式的目的就是达到命令的发出者和执行者之间解耦,实现请求和执行分开,Struts其实就是一种将请求和呈现分离的技术,其中必然涉及命令模式的思想。

19、备忘录模式(Memento)

备忘录模式是指,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态(这里的备忘录实际是指备份功能)。通俗的讲下:假设有原始类A,A中有各种属性,A可以决定需要备份的属性,备忘录类B是用来存储A的一些内部状态,类C就是一个用来存储备忘录的,且只能存储,不能修改等操作。做个图来分析一下:


Original类是原始类,里面有需要保存的属性value及创建一个备忘录类,用来保存value值。Memento类是备忘录类,Storage类是存储备忘录的类,持有Memento类的实例,该模式很好理解。直接看源码:

    public class Original {                    private String value;                    public String getValue() {              return value;          }                public void setValue(String value) {              this.value = value;          }                public Original(String value) {              this.value = value;          }                public Memento createMemento(){              return new Memento(value);          }                    public void restoreMemento(Memento memento){              this.value = memento.getValue();          }      }      public class Memento {                    private String value;                public Memento(String value) {              this.value = value;          }                public String getValue() {              return value;          }                public void setValue(String value) {              this.value = value;          }      }      public class Storage {                    private Memento memento;                    public Storage(Memento memento) {              this.memento = memento;          }                public Memento getMemento() {              return memento;          }                public void setMemento(Memento memento) {              this.memento = memento;          }      }      public class Test {                public static void main(String[] args) {                            // 创建原始类              Original origi = new Original("egg");                    // 创建备忘录              Storage storage = new Storage(origi.createMemento());                    // 修改原始类的状态              System.out.println("初始化状态为:" + origi.getValue());              origi.setValue("niu");              System.out.println("修改后的状态为:" + origi.getValue());                    // 回复原始类的状态              origi.restoreMemento(storage.getMemento());              System.out.println("恢复后的状态为:" + origi.getValue());          }      }  

输出:

初始化状态为:egg
修改后的状态为:niu
恢复后的状态为:egg

简单描述下:新建原始类时,value被初始化为egg,后经过修改,将value的值置为niu,最后进行恢复状态,结果成功恢复了。

20、状态模式(State)

状态模式是指,允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。看图:


State类是个状态类,Context类可以实现切换,我们来看看代码:

    public class State {                    private String value;                    public String getValue() {              return value;          }                public void setValue(String value) {              this.value = value;          }                public void method1(){              System.out.println("execute the first opt!");          }                    public void method2(){              System.out.println("execute the second opt!");          }      }       public class Context {                private State state;                public Context(State state) {              this.state = state;          }                public State getState() {              return state;          }                public void setState(State state) {              this.state = state;          }                public void method() {              if (state.getValue().equals("state1")) {                  state.method1();              } else if (state.getValue().equals("state2")) {                  state.method2();              }          }      }      public class Test {                public static void main(String[] args) {                            State state = new State();              Context context = new Context(state);                            //设置第一种状态              state.setValue("state1");              context.method();                            //设置第二种状态              state.setValue("state2");              context.method();          }      }  

输出:

execute the first opt!
execute the second opt!

根据这个特性,状态模式在日常开发中用的挺多的,比如有时我们希望根据对象的某一属性,区别开他们的一些功能,如说简单的权限控制等。

21、访问者模式(Visitor)

访问者模式指的是,表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式把数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化。访问者模式适用于数据结构相对稳定,但算法易于变化的系统。访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。访问者模式将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。其缺点就是增加新的数据结构很困难。简单关系图:

注:图中的getSubject()在代码中被换成了doSomething(),因为这样更能说明这是基于原数据结构的新的操作。

代码如下:

    public interface Visitor {          public void visit(Subject sub);      }      public class MyVisitor implements Visitor {                @Override          public void visit(Subject sub) {              System.out.println("visit the subject:"+sub.doSomething());          }      }      public interface Subject {          public void accept(Visitor visitor);          public String doSomething();      }      public class MySubject implements Subject {                @Override          public void accept(Visitor visitor) {              visitor.visit(this);          }                @Override          public String doSomething() {              return "love";          }      }      public class Test {                public static void main(String[] args) {                            Visitor visitor = new MyVisitor();              Subject sub = new MySubject();              sub.accept(visitor);              }      }  

输出:

visit the subject:love

在实际工作中,访问者模式通常这样使用,MySubject可能有多个子类,针对不同的子类,由不同的Vistor去实现。

可以通过下面的例子加深了解:

abstract class Element {public int a;public abstract void accept(IVisitor visitor);public abstract void doSomething();}interface IVisitor {public void visit(ConcreteElement1 el1);public void visit(ConcreteElement2 el2);}class ConcreteElement1 extends Element {public ConcreteElement1(int n){this.a=n;}public void doSomething(){System.out.print("小于等于50:");}public void accept(IVisitor visitor) {visitor.visit(this);}}class ConcreteElement2 extends Element {public ConcreteElement2(int n){this.a=n;}public void doSomething(){System.out.print("大于50:");}public void accept(IVisitor visitor) {visitor.visit(this);}}class Visitor implements IVisitor {public void visit(ConcreteElement1 el1) {el1.doSomething();System.out.println(el1.a);}public void visit(ConcreteElement2 el2) {el2.doSomething();System.out.println(el2.a);}}class ObjectStruture {public static List<Element> getList(){List<Element> list = new ArrayList<Element>();Random ran = new Random();for(int i=0; i<10; i++){int a = ran.nextInt(100);if(a<=50){list.add(new ConcreteElement1(a));}else{list.add(new ConcreteElement2(a));}}return list;}}public class Test {    public static void main(String[] args) {    List<Element> list = ObjectStruture.getList();for(Element e: list){e.accept(new Visitor());}    }}

22、中介者模式(Mediator)

中介者模式是指,用一个中介者对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使耦合松散,而且可以独立地改变它们之间的交互。中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。中介者模式又叫做调停者模式。先看看图:

代码如下:

    abstract class AbstractColleague {          protected int number;                public int getNumber() {              return number;          }                public void setNumber(int number){              this.number = number;          }          //注意这里的参数不再是同事类,而是一个中介者          public abstract void setNumber(int number, AbstractMediator am);      }            class ColleagueA extends AbstractColleague{                public void setNumber(int number, AbstractMediator am) {              this.number = number;              am.AaffectB();          }      }            class ColleagueB extends AbstractColleague{                @Override          public void setNumber(int number, AbstractMediator am) {              this.number = number;              am.BaffectA();          }      }            abstract class AbstractMediator {          protected AbstractColleague A;          protected AbstractColleague B;                    public AbstractMediator(AbstractColleague a, AbstractColleague b) {              A = a;              B = b;          }                public abstract void AaffectB();                    public abstract void BaffectA();            }      class Mediator extends AbstractMediator {                public Mediator(AbstractColleague a, AbstractColleague b) {              super(a, b);          }                //处理A对B的影响          public void AaffectB() {              int number = A.getNumber();              B.setNumber(number*100);          }                //处理B对A的影响          public void BaffectA() {              int number = B.getNumber();              A.setNumber(number/100);          }      }            public class Client {          public static void main(String[] args){              AbstractColleague collA = new ColleagueA();              AbstractColleague collB = new ColleagueB();                            AbstractMediator am = new Mediator(collA, collB);                            System.out.println("==========通过设置A影响B==========");              collA.setNumber(1000, am);              System.out.println("collA的number值为:"+collA.getNumber());              System.out.println("collB的number值为A的10倍:"+collB.getNumber());                    System.out.println("==========通过设置B影响A==========");              collB.setNumber(1000, am);              System.out.println("collB的number值为:"+collB.getNumber());              System.out.println("collA的number值为B的0.1倍:"+collA.getNumber());                        }      }  

值得一提的是,上面的示例只是两个类的交互,如果类比较多,可以把需要交互的对象放在列表中,由Mediator类统一管理。

23、解释器模式(Interpreter)

解释器模式是指,给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。参考下图:


Context类是一个上下文环境类,Plus和Minus分别是用来计算的实现,代码如下:

    public interface Expression {          public int interpret(Context context);      }      public class Plus implements Expression {                @Override          public int interpret(Context context) {              return context.getNum1()+context.getNum2();          }      }      public class Minus implements Expression {                @Override          public int interpret(Context context) {              return context.getNum1()-context.getNum2();          }      }      public class Context {                    private int num1;          private int num2;                    public Context(int num1, int num2) {              this.num1 = num1;              this.num2 = num2;          }                    public int getNum1() {              return num1;          }          public void setNum1(int num1) {              this.num1 = num1;          }          public int getNum2() {              return num2;          }          public void setNum2(int num2) {              this.num2 = num2;          }                          }      public class Test {                public static void main(String[] args) {                    // 计算9+2-8的值              int result = new Minus().interpret((new Context(new Plus()                      .interpret(new Context(9, 2)), 8)));              System.out.println(result);          }      }  

输出:3

解释器模式适用于重复发生的,简单的,语法规则。比如四则运算,公式每次都不同,有时是a+b-c*d,有时是a*b+c-d,等等等等个,公式千变万化,但是都是由加减乘除四个非终结符来连接的,这时我们就可以使用解释器模式,采用递归调用的运算方式。如果非终结符很多,显然不应该使用解释器模式。解释器模式在实际的系统开发中使用的很少,因为他会引起效率、性能以及维护等问题。解释器能解决的问题,很多开源工具包都能解决(包括前面说到的四则运算,没必要自己闭门造车),所以使用解释器模式前一定要注意使用场景是否合适。


参考地址:http://blog.csdn.net/zhangerqing/article/details/8243942

参考地址:http://blog.csdn.net/zhangerqing/article/details/8245537

参考地址:http://blog.csdn.net/zhouyong0/article/details/7909456

参考地址:http://blog.csdn.net/zhengzhb/article/details/7489639

参考地址:http://blog.csdn.net/chenhuade85/article/details/8141831

0 0
原创粉丝点击