设计模式读书笔记3

前两篇博客分别介绍了设计模式中的建造型模式和结构型模式,这次就来讲讲行为型模式。建造型模式和结构型模式分别规范了对象的构造与对象间的组合,而行为型模式的意义在于提供了对象间的通信方式。下面来看几种常见的行为型模式。

责任链模型,目的是使请求发送者与接受者解耦,通俗的说,就是将多个可能的请求接受者放在一条链表上(责任链),而发送者完全不关心接受者的内部实现,甚至不关心到底是哪个对象响应了自己的请求,它只需要将自己的请求抛出,或者说发送请求到责任链上,责任链上的一系列对象便会判断是否对请求进行处理。这个模式适用于对同一请求可能会有多个对象进行处理的情况,具体使用哪个对象在运行时判断。下面看一份源码,实现了三种类型的日志记录器,通过判断请求级别是否相同来决定是否进行记录。

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之间的交互,使得数据与显示之间完全分离,这对于应用程序开发具有很大的意义。由于该模式十分常见,在此不给出示例源码。

其他行为型模式比如空对象模式、状态模式、中介者模式等留到以后有机会再分析。总结一下行为型设计模式的核心思想:通过封装和解耦来实现对象间通信需求。

猜你喜欢

转载自www.cnblogs.com/tilmto-Jerry/p/8921279.html
今日推荐