行为型模式:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,也可以设计一个抽象类(可有可无,属于辅助类),提供辅助函数
//统一的接口 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 { public int calculate(String exp) { int arrayInt[] = split(exp,"[+]"); return arrayInt[0]+arrayInt[1]; } } public class Minus extends AbstractCalculator implements ICalculator { public int calculate(String exp) { int arrayInt[] = split(exp,"-"); return arrayInt[0]-arrayInt[1]; } } public class Multiply extends AbstractCalculator implements ICalculator { public int calculate(String exp) { int arrayInt[] = split(exp,"[*]"); return arrayInt[0]*arrayInt[1]; } } //测试类 public class Test { public static void main(String[] args) { String exp = "2+8"; ICalculator cal = new Plus(); int result = cal.calculate(exp); System.out.println(result); } }
策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。
我们之前在学校TreeSet排序的时候,有一种叫做资客户化排序的方式,就是给TreeSet传一个比较器对象,这个其实就是使用了策略模式
模板方法模式,就是指:一个抽象类中,有一个主方法,再定义1…n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类的方法,实现对子类的调用其实就是我们之前所说的: 子类重新/实现父类中的方法,那么调用该方法的时候则是调用到了子类中重写之后的方法
//父类 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 { public int calculate(int num1,int num2) { return num1 + num2; } } //测试类 public class Test { public static void main(String[] args) { String exp = "8+8"; AbstractCalculator cal = new Plus(); int result = cal.calculate(exp, "\\+"); System.out.println(result); } }
观察者模式很好理解,类似于邮件订阅和RSS订阅,当我们浏览一些博客时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。
我们在GUI那一章学习的事件监听机制就是可以这种设置模式来构建的代码
//观察者接口 public interface Observer { public void update(); } //观察者1 public class Observer1 implements Observer { public void update() { System.out.println("observer1 has received!"); } } //观察者2 public class Observer2 implements Observer { 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 Vectorvector = new Vector (); public void add(Observer observer) { vector.add(observer); } public void del(Observer observer) { vector.remove(observer); } public void notifyObservers() { Iterator it = vector.iterator(); while(it.hasNext()){ Observer next = it.next(); next.update(); } } } //我们自己的一个被观察者实现 里面可以有我们自己的各种属性和方法 public class MySubject extends AbstractSubject { public void operation() { System.out.println("update self!"); notifyObservers(); } } //测试类 public class Test { public static void main(String[] args) { Subject sub = new MySubject(); sub.add(new Observer1()); sub.add(new Observer2()); sub.operation(); } }
顾名思义,迭代器模式就是顺序访问聚集中的对象,一般来说,集合中非常常见,如果对集合类比较熟悉的话,理解本模式会十分轻松。
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"}; public Iterator iterator() { return new MyIterator(this); } public Object get(int i) { return string[i]; } 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; } public Object previous() { if(pos > 0){ pos--; } return collection.get(pos); } public Object next() { if(pos<collection.size()-1){ return="" public="" boolean="" object="" pos="0;" class="" test="" static="" void="" collection="" iterator="" it="collection.iterator();" pre="">
责任链模式,有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。 (web应该中学习到的Filter其实就是一个责任链设计模式)
public interface Handler { public void operator(); } public class MyHandler implements Handler { private String name; private Handler handler; public MyHandler(String name) { this.name = name; } public Handler getHandler() { return handler; } public void setHandler(Handler handler) { this.handler = handler; } public void operator() { System.out.println("name = "+name); 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(); } }
命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者(司令、命令、士兵)相互解耦,任何一方都不用去依赖其他人的具体实现,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。
//命令执行的接口 public interface Command { public void exe(); } //具体实现的命令 public class MyCommand implements Command { private Receiver receiver; public MyCommand(Receiver receiver) { this.receiver = receiver; } 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(); } }
这个很好理解,命令模式的目的就是达到命令的发出者和执行者之间解耦,实现请求和执行分开。 对于大多数请求-响应模式的功能,比较适合使用命令模式。
也可以叫备份模式,主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象:假设有原始类A,A中有各种属性,A可以决定需要备份的属性,备忘录类B是用来存储A的一些内部状态,类C呢,就是一个用来存储备忘录的,且只能存储,不能修改等操作。
//原始类,里面有需要保存的属性value 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(); } } //备忘录类,用来保存value值 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; } } //存储备忘录的类,持有Memento类的实例 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()); } }
核心思想就是:当对象的状态改变时,同时改变其行为,很好理解!就拿QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作。再比如交通灯,有红黄绿三种状态,每种状态下操作也是不一样的
//状态类 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!"); } } //Context类可以实现切换状态 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(); } }
访问者模式把数据结构和作用于结构上的操作解耦合,使得对数据操作可相对自由地演化。访问者模式适用于数据结构相对稳定,算法又易变化的系统。
因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。访问者模式将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。其缺点就是增加新的数据结构很困难。
简单来说,访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。
//访问者接口 public interface Visitor { public void visit(Subject sub); } //访问者的一个具体实现 public class MyVisitor implements Visitor { public void visit(Subject sub) { System.out.println("visit the subject:"+sub.getSubject()); } } //被访问者接口 public interface Subject { public void accept(Visitor visitor); public String getSubject(); } //被访问者的一个具体实现 public class MySubject implements Subject { public void accept(Visitor visitor) { visitor.visit(this); } public String getSubject() { return "love"; } } //测试类 public class Test { public static void main(String[] args) { Visitor visitor = new MyVisitor(); Subject sub = new MySubject(); sub.accept(visitor); } }
该模式适用场景:如果我们想为一个现有的类增加新功能,不得不考虑几个事情:
1、新功能会不会与现有功能出现兼容性问题? 2、以后会不会再需要添加? 3、如果类不允许修改代码怎么办?
面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦
这个模式也是用来降低类和类之间的耦合的,因为如果类和类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和Mediator类的关系,具体类类之间的关系及调度交给Mediator就行
//中间者接口 public interface Mediator { public void createMediator(); public void workAll(); } //中介者的一个具体实现 public class MyMediator implements Mediator { private User user1; private User user2; public User getUser1() { return user1; } public User getUser2() { return user2; } public void createMediator() { user1 = new User1(this); user2 = new User2(this); } public void workAll() { user1.work(); user2.work(); } } //抽象类 public abstract class User { private Mediator mediator; public Mediator getMediator(){ return mediator; } public User(Mediator mediator) { this.mediator = mediator; } public abstract void work(); } //User1 public class User1 extends User { public User1(Mediator mediator){ super(mediator); } public void work() { System.out.println("user1 exe!"); } } //User2 public class User2 extends User { public User2(Mediator mediator){ super(mediator); } public void work() { System.out.println("user2 exe!"); } } //测试类 public class Test { public static void main(String[] args) { Mediator mediator = new MyMediator(); mediator.createMediator(); mediator.workAll(); } }
适用场景 在面向对象编程中,一个类必然会与其他的类发生依赖关系,完全独立的类是没有意义的。一个类同时依赖多个类的情况也相当普遍,既然存在这样的情况,说明,一对多的依赖关系有它的合理性,适当的使用中介者模式可以使原本凌乱的对象关系清晰,但是如果滥用,则可能会带来反的效果。一般来说,只有对于那种同事类之间是网状结构的关系,才会考虑使用中介者模式。可以将网状结构变为星状结构,使同事类之间的关系变的清晰一些。
在以下情况下可以使用解释器模式:
有一个简单的语法规则,比如一个sql语句,如果我们需要根据sql语句进行其他语言的转换,就可以使用解释器模式来对语句进行解释。 一些重复发生的问题,比如加减乘除四则运算,但是公式每次都不同,有时是a+b-c*d,有时是a*b+c-d,等等等等个,公式千变万化,但是都是由加减乘除四个非终结符来连接的,这时我们就可以使用解释器模式。//解释器接口(这里的是专门解析数学运算表达式) public interface Expression { public int interpret(Context context); } //加法 public class Plus implements Expression { public int interpret(Context context) { return context.getNum1()+context.getNum2(); } } //减法 public class Minus implements Expression { public int interpret(Context context) { return context.getNum1()-context.getNum2(); } } //Context类是一个上下文环境类 持有运行中所需的数据 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)); //相当于:new Minus().interpret(new Context(11, 8)); System.out.println(result); } }