zoukankan      html  css  js  c++  java
  • Java学习--设计模式之行为型模式(一)

    一、简介

      行为型模式:这些设计模式特别关注对象之间的通信。包括:责任链模式(Chain of Responsibility Pattern)、命令模式(Command Pattern)、解释器模式(Interpreter Pattern)、迭代器模式(Iterator Pattern)、中介者模式(Mediator Pattern)、备忘录模式(Memento Pattern)、观察者模式(Observer Pattern)、状态模式(State Pattern)、空对象模式(Null Object Pattern)、策略模式(Strategy Pattern)、模板模式(Template Pattern)、访问者模式(Visitor Pattern);

    二、责任链模式(Chain of Responsibility Pattern)

      1、概念

       顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

      2、简介

       意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

       主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

       何时使用:在处理消息的时候以过滤很多道。

       如何解决:拦截的类都实现统一接口。

       关键代码:Handler 里面聚合它自己,在 HanleRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。

       应用实例: 

        (1)、JS 中的事件冒泡。

        (2)、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。

       优点: 

        (1)、降低耦合度。它将请求的发送者和接收者解耦。

        (2)、简化了对象。使得对象不需要知道链的结构。

        (3)、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。

        (4)、增加新的请求处理类很方便。

       缺点: 

        (1)、不能保证请求一定被接收。

        (2)、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。

        (3)、可能不容易观察运行时的特征,有碍于除错。

       使用场景: 

        (1)、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。

        (2)、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

        (3)、可动态指定一组对象处理请求。

       注意事项:在 JAVA WEB 中遇到很多应用。

      3、实例

       我们创建抽象类 AbstractLogger,带有详细的日志记录级别。然后我们创建三种类型的记录器,都扩展了 AbstractLogger。每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器。

        

        (1)、创建抽象的记录器类

    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);
        
    }

        (2)、创建扩展了该记录类的实体类

    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);
       }
    }

        (3)、创建不同类型的记录器。赋予它们不同的错误级别,并在每个记录器中设置下一个记录器。每个记录器中的下一个记录器代表的是链的一部分。

    public class ChainPatternDemo {
        
       private static AbstractLogger getChainOfLoggers(){
    
          AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
          AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
          AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
    
          errorLogger.setNextLogger(fileLogger);
          fileLogger.setNextLogger(consoleLogger);
    
          return errorLogger;    
       }
    
       public static void main(String[] args) {
          AbstractLogger loggerChain = getChainOfLoggers();
    
          loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");
    
          loggerChain.logMessage(AbstractLogger.DEBUG,  "This is an debug level information.");
    
          loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information.");
       }
    }

        (4)、验证输出

    1 Standard Console::Logger: This is an information.
    2 File::Logger: This is an debug level information.
    3 Standard Console::Logger: This is an debug level information.
    4 Error Console::Logger: This is an error information.
    5 File::Logger: This is an error information.
    6 Standard Console::Logger: This is an error information.

    三、命令模式(Command Pattern)

      1、概念

       命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

      2、简介

       意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。

       主要解决:在软件系统中,行为请求者与实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

       如何解决:通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。

       优点: 

        (1)、降低了系统耦合度。

        (2)、新的命令可以很容易添加到系统中去。

       缺点:使用命令模式可能会导致某些系统有过多的具体命令类。

       使用场景:认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。

       注意事项:系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,见命令模式的扩展。

      3、实例

       我们首先创建作为命令的接口 Order,然后创建作为请求的 Stock 类。实体命令类 BuyStock 和 SellStock,实现了 Order 接口,将执行实际的命令处理。创建作为调用对象的类 Broker,它接受订单并能下订单。Broker 对象使用命令模式,基于命令的类型确定哪个对象执行哪个命令。演示类 CommandPatternDemo,我们的演示类使用 Broker 类来演示命令模式。

        

        (1)、创建一个命令接口

    public interface Order {
       void execute();
    }

        (2)、创建一个请求类

    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");
       }
    }

        (3)、创建实现了 Order 接口的实体类

    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();
       }
    }

        (4)、创建命令调用类

    import java.util.ArrayList;
    import java.util.List;
    
       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();
       }
    }

        (5)、使用 Borker 类来接受并执行命令

    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();
       }
    }

        (6)、演示结果

    1 Stock [ Name: ABC, Quantity: 10 ] bought
    2 Stock [ Name: ABC, Quantity: 10 ] sold

    四、解释器模式(Interpreter Pattern)

      1、概念

       解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

      2、简介

       意图:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。

       主要解决:对于一些固定文法构建一个解释句子的解释器。

       何时使用:如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

       如何解决:构建语法树,定义终结符与非终结符。

       关键代码:构件环境类,包含解释器之外的一些全局信息,一般是 HashMap。

       应用实例:编译器、运算表达式计算。

       优点: 

        (1)、可扩展性比较好,灵活。

        (2)、增加了新的解释表达式的方式。

        (3)、易于实现简单文法。

       缺点: 

        (1)、可利用场景比较少。

        (2)、对于复杂的文法比较难维护。

        (3)、解释器模式会引起类膨胀。

        (4)、解释器模式采用递归调用方法。

       使用场景: 

        (1)、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。

        (2)、一些重复出现的问题可以用一种简单的语言来进行表达。

        (3)、一个简单语法需要解释的场景。

       注意事项:可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。

      3、实例

       我们将创建一个接口 Expression 和实现了 Expression 接口的实体类。定义作为上下文中主要解释器的 TerminalExpression 类。其他的类 OrExpression、AndExpression 用于创建组合式表达式。演示类 InterpreterPatternDemo,我们的演示类使用 Expression 类创建规则和演示表达式的解析。

        

        (1)、创建一个表达式接口

    public interface Expression {
       public boolean interpret(String context);
    }

        (2)、创建实现了 Expression 接口的实体类

    public class TerminalExpression implements Expression {
        
       private String data;
    
       public TerminalExpression(String data){
          this.data = data; 
       }
    
       @Override
       public boolean interpret(String context) {
          if(context.contains(data)){
             return true;
          }
          return false;
       }
    }
    public class OrExpression implements Expression {
         
       private Expression expr1 = null;
       private Expression expr2 = null;
    
       public OrExpression(Expression expr1, Expression expr2) { 
          this.expr1 = expr1;
          this.expr2 = expr2;
       }
    
       @Override
       public boolean interpret(String context) {        
          return expr1.interpret(context) || expr2.interpret(context);
       }
    }
    public class AndExpression implements Expression {
         
       private Expression expr1 = null;
       private Expression expr2 = null;
    
       public AndExpression(Expression expr1, Expression expr2) { 
          this.expr1 = expr1;
          this.expr2 = expr2;
       }
    
       @Override
       public boolean interpret(String context) {        
          return expr1.interpret(context) && expr2.interpret(context);
       }
    }

        (3)、InterpreterPatternDemo 使用 Expression 类来创建规则,并解析它们。

    public class InterpreterPatternDemo {
    
       //规则:Robert 和 John 是男性
       public static Expression getMaleExpression(){
          Expression robert = new TerminalExpression("Robert");
          Expression john = new TerminalExpression("John");
          return new OrExpression(robert, john);        
       }
    
       //规则:Julie 是一个已婚的女性
       public static Expression getMarriedWomanExpression(){
          Expression julie = new TerminalExpression("Julie");
          Expression married = new TerminalExpression("Married");
          return new AndExpression(julie, married);        
       }
    
       public static void main(String[] args) {
          Expression isMale = getMaleExpression();
          Expression isMarriedWoman = getMarriedWomanExpression();
    
          System.out.println("John is male? " + isMale.interpret("John"));
          System.out.println("Julie is a married women? " + isMarriedWoman.interpret("Married Julie"));
       }
    }

        (4)、演示结果

    1 John is male? true
    2 Julie is a married women? true

    五、迭代器模式(Iterator Pattern)

      1、概念

       迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。迭代器模式属于行为型模式。

      2、简介

       意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

       主要解决:不同的方式来遍历整个整合对象。

       何时使用:遍历一个聚合对象。

       如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。

       应用实例:JAVA 中的 iterator。

       优点: 

        (1)、它支持以不同的方式遍历一个聚合对象。

        (2)、迭代器简化了聚合类。

        (3)、在同一个聚合上可以有多个遍历。

        (4)、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。

       缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

       使用场景: 

        (1)、访问一个聚合对象的内容而无须暴露它的内部表示。

        (2)、需要为聚合对象提供多种遍历方式。

        (3)、为遍历不同的聚合结构提供一个统一的接口。

       注意事项:迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

      3、实例

       我们将创建一个叙述导航方法的 Iterator 接口和一个返回迭代器的 Container 接口。实现了 Container 接口的实体类将负责实现 Iterator 接口。演示类 IteratorPatternDemo,我们的演示类使用实体类 NamesRepository 来打印 NamesRepository 中存储为集合的 Names。

        

        (1)、创建接口

    public interface Iterator {
       public boolean hasNext();
       public Object next();
    }

        (2)、创建实现了 Container 接口的实体类。该类有实现了 Iterator 接口的内部类 NameIterator。

    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;
          }        
       }
    }

        (3)、使用 NameRepository 来获取迭代器,并打印名字。

    public class IteratorPatternDemo {
        
       public static void main(String[] args) {
          NameRepository namesRepository = new NameRepository();
    
          for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
             String name = (String)iter.next();
             System.out.println("Name : " + name);
          }     
       }
    }

        (4)、演示结果

    1 Name : Robert
    2 Name : John
    3 Name : Julie
    4 Name : Lora

    PS:因作者能力有限,如有误还请谅解;

  • 相关阅读:
    hihoCoder #1062 : 最近公共祖先·一
    hihoCoder #1050 : 树中的最长路
    hihoCoder #1049 : 后序遍历
    108 Convert Sorted Array to Binary Search Tree 将有序数组转换为二叉搜索树
    107 Binary Tree Level Order Traversal II 二叉树的层次遍历 II
    106 Construct Binary Tree from Inorder and Postorder Traversal 从中序与后序遍历序列构造二叉树
    105 Construct Binary Tree from Preorder and Inorder Traversal 从前序与中序遍历序列构造二叉树
    104 Maximum Depth of Binary Tree 二叉树的最大深度
    102 Binary Tree Level Order Traversal 二叉树的层次遍历
    101 Symmetric Tree 判断一颗二叉树是否是镜像二叉树
  • 原文地址:https://www.cnblogs.com/WHL5/p/9198858.html
Copyright © 2011-2022 走看看