前两篇博客分别介绍了设计模式中的建造型模式和结构型模式,这次就来讲讲行为型模式。建造型模式和结构型模式分别规范了对象的构造与对象间的组合,而行为型模式的意义在于提供了对象间的通信方式。下面来看几种常见的行为型模式。
责任链模型,目的是使请求发送者与接受者解耦,通俗的说,就是将多个可能的请求接受者放在一条链表上(责任链),而发送者完全不关心接受者的内部实现,甚至不关心到底是哪个对象响应了自己的请求,它只需要将自己的请求抛出,或者说发送请求到责任链上,责任链上的一系列对象便会判断是否对请求进行处理。这个模式适用于对同一请求可能会有多个对象进行处理的情况,具体使用哪个对象在运行时判断。下面看一份源码,实现了三种类型的日志记录器,通过判断请求级别是否相同来决定是否进行记录。
public abstract class AbstractLogger { public static int INFO = 1; public static int DEBUG = 2; public static int ERROR = 3; protected int level; protected AbstractLogger nextLogger; public void setNextLogger(AbstractLogger nextLogger){ this.nextLogger = nextLogger; } public void logMessage(int level, String message){ if(this.level == level){ write(message); } if(nextLogger !=null){ nextLogger.logMessage(level, message); } } abstract protected void write(String message); }
public class ConsoleLogger extends AbstractLogger { public ConsoleLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Standard Console::Logger: " + message); } } public class ErrorLogger extends AbstractLogger { public ErrorLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Error Console::Logger: " + message); } } public class FileLogger extends AbstractLogger { public FileLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("File::Logger: " + message); } }
通过以上的数据结构便可以将三种日志记录器设置在一条链表上,对第一个结点使用 logMessage 函数就是把请求发送到责任链上,每个接受对象判断是否进行处理。
命令模式是另一种让行为请求者和行为实现者解耦的方式,实现方式是将请求以命令的形式包裹在对象中,并传给调用对象。命令模式的核心在于实现三个角色:命令真正执行者、命令、调用者,将这三者分别实现后大大降低了行为请求与实现的耦合度,且可以很方便地添加新命令。例如要实现股票的买卖请求:
public interface Order { void execute(); }
public class Stock { private String name = "ABC"; private int quantity = 10; public void buy(){ System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] bought"); } public void sell(){ System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] sold"); } }
public class BuyStock implements Order { private Stock abcStock; public BuyStock(Stock abcStock){ this.abcStock = abcStock; } public void execute() { abcStock.buy(); } }
public class SellStock implements Order { private Stock abcStock; public SellStock(Stock abcStock){ this.abcStock = abcStock; } public void execute() { abcStock.sell(); } }
public class Broker { private List<Order> orderList = new ArrayList<Order>(); public void takeOrder(Order order){ orderList.add(order); } public void placeOrders(){ for (Order order : orderList) { order.execute(); } orderList.clear(); } }
public class CommandPatternDemo { public static void main(String[] args) { Stock abcStock = new Stock(); BuyStock buyStockOrder = new BuyStock(abcStock); SellStock sellStockOrder = new SellStock(abcStock); Broker broker = new Broker(); broker.takeOrder(buyStockOrder); broker.takeOrder(sellStockOrder); broker.placeOrders(); } }
上述代码中, Stock 类是命名的真正执行者,而 BuyStock 类和 SellStock 类分别实现了 Order 接口,承担了命令的角色, Broker 类是命令调用者,客户需要执行什么操作,只需将命令对象添加到调用者对象并让其执行即可。
接着看看迭代器模式,即遍历一个聚合对象的方式,Java中的每个集合类都有非常成熟的迭代器来进行遍历操作,这里就给出自定义对象的迭代器模式。
public interface Iterator { public boolean hasNext(); public Object next(); }
public interface Container { public Iterator getIterator(); }
public class NameRepository implements Container { public String names[] = {"Robert" , "John" ,"Julie" , "Lora"}; @Override public Iterator getIterator() { return new NameIterator(); } private class NameIterator implements Iterator { int index; @Override public boolean hasNext() { if(index < names.length){ return true; } return false; } @Override public Object next() { if(this.hasNext()){ return names[index++]; } return null; } } }
这里用到了 java 中 private 内部类,仅在所属外部类中可见。一个迭代器类需要实现 hasNext 函数判断是否还有下一元素,以及 next 函数返回下一元素,而使用迭代器的集合类需要实现 getIterator 函数得到该集合的迭代器。这样一个迭代器模式就完成了。
再来看备忘录模式,其保存一个对象当前的状态以便以后恢复对象,就像游戏中的恢复存档以及数据库的事务管理等,给用户提供了一种可以恢复状态的机制。一种好的备忘录模式需要实现信息封装,使得用户不需要关心状态的保存细节。Java中可建立三个类来实现备忘录模式:状态类(保存状态信息)、状态创建类(用于创建一个状态)、状态保存类(建立一个集合保存过往的状态)。示例代码如下:
public class Memento { private String state; public Memento(String state){ this.state = state; } public String getState(){ return state; } }
public class Originator { private String state; public void setState(String state){ this.state = state; } public String getState(){ return state; } public Memento saveStateToMemento(){ return new Memento(state); } public void getStateFromMemento(Memento Memento){ state = Memento.getState(); } }
public class CareTaker { private List<Memento> mementoList = new ArrayList<Memento>(); public void add(Memento state){ mementoList.add(state); } public Memento get(int index){ return mementoList.get(index); } }
以上便是三种类的定义,需要保存状态时调用 Originator 类的 saveStateToMemento 函数并 add 到 CareTaker 对象的 ArrayList 中。
接着看看模板模式,一种经常出现的设计模式,很多经常使用它的人甚至不觉得这算是一种“高大上”的设计模式。模板模式使用一个抽象类(模板)定义了执行它的方法的方式,它的子类可以按需要重写方法实现,说白了,就是规范了抽象类该怎么使用。其优点是可在不变部分的基础上进行扩展 ,且提取了公共代码,便于维护。示例代码:
public abstract class Game { abstract void initialize(); abstract void startPlay(); abstract void endPlay(); public final void play(){ initialize(); startPlay(); endPlay(); } }
public class Football extends Game { @Override void endPlay() { System.out.println("Football Game Finished!"); } @Override void initialize() { System.out.println("Football Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Football Game Started. Enjoy the game!"); } }
这里要强调的是模板类 play 函数前的 final 修饰符,该修饰符的含义是此函数不能被重写,保证了抽象类中不变的部分不可改变,防止子类恶意修改,这也是定义模板时的重要原则,即公共不可变部分用 final 修饰。这一点在我以前的编程经验中是从未想到过的,可见该设计模式具有很重要的意义。
下面说说界面编程中经常出现的观察者模式,该模式描述的是对象间的一对多关系,即当一个对象被修改时,则会自动通知它的依赖对象。这一模式在GUI编程中经常出现,比如Qt的信号槽机制,Android中的Rxjava库。该模式提供的是一般性触发机制的方案。下面给出被观察者的源码(“一对多”关系中的“一”):
public class Subject { private List<Observer> observers = new ArrayList<Observer>(); private int state; public int getState() { return state; } public void setState(int state) { this.state = state; notifyAllObservers(); } public void attach(Observer observer){ observers.add(observer); } public void notifyAllObservers(){ for (Observer observer : observers) { observer.update(); } } }
给出了 attach 函数和 notifyAllObservers 函数后,观察者模式的原理就一目了然了,剩下的工作就是对不同的观察者实现 update 函数。
最后再看一种经典的设计模式:MVC模式,即Model-View-Controller(模型-视图-控制器) 模式。这种模式经常在各个场合被提起,其意义在于很好地实现了应用程序各模块之间的解耦。Model代表存取数据的对象,View表示数据可视化对象,Controller负责Model和View之间的交互,使得数据与显示之间完全分离,这对于应用程序开发具有很大的意义。由于该模式十分常见,在此不给出示例源码。
其他行为型模式比如空对象模式、状态模式、中介者模式等留到以后有机会再分析。总结一下行为型设计模式的核心思想:通过封装和解耦来实现对象间通信需求。