二、行为型模式
一共十二种
2.1 责任链模式
(1)概念
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
(2)适用场景
1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
3、可动态指定一组对象处理请求。
**注意事项:**在 JAVA WEB 中遇到很多应用。
(3)代码示例
我们创建抽象类 AbstractLogger,带有详细的日志记录级别。然后我们创建三种类型的记录器,都扩展了 AbstractLogger。每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器。
创建抽象的记录器类。
package com.alibaba.design.chainofresponsibilitypattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-21:50 */ 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); }
创建扩展了该记录器类的实体类。
- ConsoleLogger
package com.alibaba.design.chainofresponsibilitypattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-21:50 */ 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); } }
- ErrorLogger
package com.alibaba.design.chainofresponsibilitypattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-21:51 */ 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); } }
- FileLogger
package com.alibaba.design.chainofresponsibilitypattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-21:51 */ public class FileLogger extends AbstractLogger { public FileLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("File::Logger: " + message); } }
客户端测试类
package com.alibaba.design.chainofresponsibilitypattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-21:52 */ 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 a debug level information."); loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information."); } }
(4)该模式在源码中的体现
责任链模式在Tomcat中的应用
参考 https://www.cnblogs.com/java-my-life/archive/2012/05/28/2516865.html
众所周知Tomcat中的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 { } }
使用DEBUG模式所看到的结果如下
实在真正执行到TestFilter类之前,会经过很多Tomcat内部的类。顺带提一下其实Tomcat的容器设置也是责任链模式,注意被红色方框所圈中的类,从Engine到Host再到Context一直到Wrapper都是通过一个链传递请求。被绿色方框所圈中的地方有一个名为ApplicationFilterChain的类,ApplicationFilterChain类所扮演的就是抽象处理者角色,而具体处理者角色由各个Filter扮演。
第一个疑问是ApplicationFilterChain将所有的Filter存放在哪里?
答案是保存在ApplicationFilterChain类中的一个ApplicationFilterConfig对象的数组中。
/** * Filters. */ private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
那ApplicationFilterConfig对象又是什么呢?
ApplicationFilterConfig是一个Filter容器。以下是ApplicationFilterConfig类的声明:
/** * Implementation of a <code>javax.servlet.FilterConfig</code> useful in * managing the filter instances instantiated when a web application * is first started. * * @author Craig R. McClanahan * @version $Id: ApplicationFilterConfig.java 1201569 2011-11-14 01:36:07Z kkolinko $ */
当一个web应用首次启动时ApplicationFilterConfig会自动实例化,它会从该web应用的web.xml文件中读取配置的Filter的信息,然后装进该容器。
刚刚看到在ApplicationFilterChain类中所创建的ApplicationFilterConfig数组长度为零,那它是在什么时候被重新赋值的呢?
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0]; • 1
是在调用ApplicationFilterChain类的addFilter()方法时。
/** * The int which gives the current number of filters in the chain. */ private int n = 0; public static final int INCREMENT = 10; void addFilter(ApplicationFilterConfig filterConfig) { // Prevent the same filter being added multiple times for(ApplicationFilterConfig filter:filters) if(filter==filterConfig) return; if (n == filters.length) { ApplicationFilterConfig[] newFilters = new ApplicationFilterConfig[n + INCREMENT]; System.arraycopy(filters, 0, newFilters, 0, n); filters = newFilters; } filters[n++] = filterConfig; }
变量n用来记录当前过滤器链里面拥有的过滤器数目,默认情况下n等于0,ApplicationFilterConfig对象数组的长度也等于0,所以当第一次调用addFilter()方法时,if (n == filters.length)的条件成立,ApplicationFilterConfig数组长度被改变。之后filters[n++] = filterConfig;将变量filterConfig放入ApplicationFilterConfig数组中并将当前过滤器链里面拥有的过滤器数目+1。
那ApplicationFilterChain的addFilter()方法又是在什么地方被调用的呢?
是在ApplicationFilterFactory类的createFilterChain()方法中。
public ApplicationFilterChain createFilterChain (ServletRequest request, Wrapper wrapper, Servlet servlet) { // get the dispatcher type DispatcherType dispatcher = null; if (request.getAttribute(DISPATCHER_TYPE_ATTR) != null) { dispatcher = (DispatcherType) request.getAttribute(DISPATCHER_TYPE_ATTR); } String requestPath = null; Object attribute = request.getAttribute(DISPATCHER_REQUEST_PATH_ATTR); if (attribute != null){ requestPath = attribute.toString(); } // If there is no servlet to execute, return null if (servlet == null) return (null); boolean comet = false; // Create and initialize a filter chain object ApplicationFilterChain filterChain = null; if (request instanceof Request) { Request req = (Request) request; comet = req.isComet(); if (Globals.IS_SECURITY_ENABLED) { // Security: Do not recycle filterChain = new ApplicationFilterChain(); if (comet) { req.setFilterChain(filterChain); } } else { filterChain = (ApplicationFilterChain) req.getFilterChain(); if (filterChain == null) { filterChain = new ApplicationFilterChain(); req.setFilterChain(filterChain); } } } else { // Request dispatcher in use filterChain = new ApplicationFilterChain(); } filterChain.setServlet(servlet); filterChain.setSupport (((StandardWrapper)wrapper).getInstanceSupport()); // Acquire the filter mappings for this Context StandardContext context = (StandardContext) wrapper.getParent(); FilterMap filterMaps[] = context.findFilterMaps(); // If there are no filter mappings, we are done if ((filterMaps == null) || (filterMaps.length == 0)) return (filterChain); // Acquire the information we will need to match filter mappings String servletName = wrapper.getName(); // Add the relevant path-mapped filters to this filter chain for (int i = 0; i < filterMaps.length; i++) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) { continue; } if (!matchFiltersURL(filterMaps[i], requestPath)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) { // FIXME - log configuration problem continue; } boolean isCometFilter = false; if (comet) { try { isCometFilter = filterConfig.getFilter() instanceof CometFilter; } catch (Exception e) { // Note: The try catch is there because getFilter has a lot of // declared exceptions. However, the filter is allocated much // earlier Throwable t = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(t); } if (isCometFilter) { filterChain.addFilter(filterConfig); } } else { filterChain.addFilter(filterConfig); } } // Add filters that match on servlet name second for (int i = 0; i < filterMaps.length; i++) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) { continue; } if (!matchFiltersServlet(filterMaps[i], servletName)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) { // FIXME - log configuration problem continue; } boolean isCometFilter = false; if (comet) { try { isCometFilter = filterConfig.getFilter() instanceof CometFilter; } catch (Exception e) { // Note: The try catch is there because getFilter has a lot of // declared exceptions. However, the filter is allocated much // earlier } if (isCometFilter) { filterChain.addFilter(filterConfig); } } else { filterChain.addFilter(filterConfig); } } // Return the completed filter chain return (filterChain); }
可以将如上代码分为两段,51行之前为第一段,51行之后为第二段。
第一段的主要目的是创建ApplicationFilterChain对象以及一些参数设置。
第二段的主要目的是从上下文中获取所有Filter信息,之后使用for循环遍历并调用filterChain.addFilter(filterConfig);将filterConfig放入ApplicationFilterChain对象的ApplicationFilterConfig数组中。
那ApplicationFilterFactory类的createFilterChain()方法又是在什么地方被调用的呢?
是在StandardWrapperValue类的invoke()方法中被调用的。
由于invoke()方法较长,所以将很多地方省略。
public final void invoke(Request request, Response response) throws IOException, ServletException { ...省略中间代码 // Create the filter chain for this request ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance(); ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet); ...省略中间代码 filterChain.doFilter(request.getRequest(), response.getResponse()); ...省略中间代码 }
那正常的流程应该是这样的:
在StandardWrapperValue类的invoke()方法中调用ApplicationFilterChai类的createFilterChain()方法———>在ApplicationFilterChai类的createFilterChain()方法中调用ApplicationFilterChain类的addFilter()方法———>在ApplicationFilterChain类的addFilter()方法中给ApplicationFilterConfig数组赋值。
根据上面的代码可以看出StandardWrapperValue类的invoke()方法在执行完createFilterChain()方法后,会继续执行ApplicationFilterChain类的doFilter()方法,然后在doFilter()方法中会调用internalDoFilter()方法。
以下是internalDoFilter()方法的部分代码
// Call the next filter if there is one if (pos < n) { //拿到下一个Filter,将指针向下移动一位 //pos它来标识当前ApplicationFilterChain(当前过滤器链)执行到哪个过滤器 ApplicationFilterConfig filterConfig = filters[pos++]; Filter filter = null; try { //获取当前指向的Filter的实例 filter = filterConfig.getFilter(); support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT, filter, request, response); if (request.isAsyncSupported() && "false".equalsIgnoreCase( filterConfig.getFilterDef().getAsyncSupported())) { request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE); } if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res, this}; SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal); } else { //调用Filter的doFilter()方法 filter.doFilter(request, response, this); }
这里的filter.doFilter(request, response, this);就是调用我们前面创建的TestFilter中的doFilter()方法。而TestFilter中的doFilter()方法会继续调用chain.doFilter(request, response);方法,而这个chain其实就是ApplicationFilterChain,所以调用过程又回到了上面调用dofilter和调用internalDoFilter方法,这样执行直到里面的过滤器全部执行。
如果定义两个过滤器,则Debug结果如下:
责任链模式在 Android 中的体现
ViewGroup 事件传递
还记得 Android 总的事件分发机制吗,主要有三个方法,dispatchTouchEvent
,onInterceptTouchEvent
,onTouchEvent
三个方法
- dispatchTouchEvent ,这个方法主要是用来分发事件的
- onInterceptTouchEvent,这个方法主要是用来拦截事件的(需要注意的是ViewGroup才有这个方法,View没有onInterceptTouchEvent这个方法
- onTouchEvent这个方法主要是用来处理事件的
- requestDisallowInterceptTouchEvent(true),这个方法能够影响父View是否拦截事件,true表示 不拦截事件,false表示拦截事件
下面引用图解 Android 事件分发机制这一篇博客的内容
当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View,TouchEvent最先到达最顶层 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法进行分发,
- 如果dispatchTouchEvent返回true 消费事件,事件终结。
- 如果dispatchTouchEvent返回 false ,则回传给父View的onTouchEvent事件处理;
- onTouchEvent事件返回true,事件终结,返回false,交给父View的OnTouchEvent方法处理
- 如果dispatchTouchEvent返回super的话,默认会调用自己的onInterceptTouchEvent方法
- 默认的情况下interceptTouchEvent回调用super方法,super方法默认返回false,所以会交给子View的onDispatchTouchEvent方法处理
- 如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,
- 如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。
通过这样链式的设计,确保了每一个 View 都有机会处理 touch 事件。如果中途有 View 处理了事件,就停止处理。
有序广播
Android 中的 BroastCast 分为两种,一种时普通广播,另一种是有序广播。普通广播是异步的,发出时可以被所有的接收者收到。而有序广播是根据优先级一次传播的,直到有接收者将其终止或者所有接收者都不终止它。有序广播的这一特性与我们的责任链模式很相近,我们可以轻松地实现一种全局的责任链事件处理。
(5)责任链模式的优缺点
- 优点:
1、降低耦合度。它将请求的发送者和接收者解耦。
2、简化了对象。使得对象不需要知道链的结构。
3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
4、增加新的请求处理类很方便。 - 缺点:
1、不能保证请求一定被接收。
2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
3、可能不容易观察运行时的特征,有碍于除错。
2.2 命令模式
(1)概念
命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
(2)适用场景
认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。
在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
**注意事项:**系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,见命令模式的扩展。
(3)代码示例
关键代码:定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口
我们首先创建作为命令的接口 Order,然后创建作为请求的 Stock 类。实体命令类 BuyStock 和 SellStock,实现了 Order 接口,将执行实际的命令处理。创建作为调用对象的类 Broker,它接受订单并能下订单。
Broker 对象使用命令模式,基于命令的类型确定哪个对象执行哪个命令。CommandPatternDemo,我们的演示类使用 Broker 类来演示命令模式。
创建一个命令接口。
package com.alibaba.design.commandpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-8:57 */ public interface Order { void execute(); }
创建一个请求类。
package com.alibaba.design.commandpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-8:57 */ public class Stock { private String name = "Tom"; 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"); } }
创建实现了 Order 接口的实体类。
- BuyStock
package com.alibaba.design.commandpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-8:59 */ public class BuyStock implements Order { private Stock abcStock; public BuyStock(Stock abcStock){ this.abcStock = abcStock; } @Override public void execute() { abcStock.buy(); } }
- SellStock
package com.alibaba.design.commandpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-9:00 */ public class SellStock implements Order { private Stock abcStock; public SellStock(Stock abcStock){ this.abcStock = abcStock; } @Override public void execute() { abcStock.sell(); } }
创建命令调用类。
package com.alibaba.design.commandpattern; import java.util.ArrayList; import java.util.List; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-9:01 */ 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(); } }
package com.alibaba.design.commandpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-9:02 */ 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(); } }
执行程序,输出结果:
(4)该模式在源码中的应用
典型的java.lang.Runable和线程池中都有用到命令模式
来看一下Runable的源码
Runable:任务抽象,也就是“命令”;线程池通过submit和execute调用
java.util.concurrent.ThreadPoolExecutor线程池中的源码
/** * Executes the given task sometime in the future. The task * may execute in a new thread or in an existing pooled thread. * * If the task cannot be submitted for execution, either because this * executor has been shutdown or because its capacity has been reached, * the task is handled by the current {@code RejectedExecutionHandler}. * * @param command the task to execute * @throws RejectedExecutionException at discretion of * {@code RejectedExecutionHandler}, if the task * cannot be accepted for execution * @throws NullPointerException if {@code command} is null */ public void execute(Runnable command) { if (command == null) throw new NullPointerException(); /* * Proceed in 3 steps: * * 1. If fewer than corePoolSize threads are running, try to * start a new thread with the given command as its first * task. The call to addWorker atomically checks runState and * workerCount, and so prevents false alarms that would add * threads when it shouldn't, by returning false. * * 2. If a task can be successfully queued, then we still need * to double-check whether we should have added a thread * (because existing ones died since last checking) or that * the pool shut down since entry into this method. So we * recheck state and if necessary roll back the enqueuing if * stopped, or start a new thread if there are none. * * 3. If we cannot queue task, then we try to add a new * thread. If it fails, we know we are shut down or saturated * and so reject the task. */ int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); } else if (!addWorker(command, false)) reject(command); } /** * Initiates an orderly shutdown in which previously submitted * tasks are executed, but no new tasks will be accepted. * Invocation has no additional effect if already shut down. * * <p>This method does not wait for previously submitted tasks to * complete execution. Use {@link #awaitTermination awaitTermination} * to do that. * * @throws SecurityException {@inheritDoc} */ public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(SHUTDOWN); interruptIdleWorkers(); onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } tryTerminate(); }
(5)命令模式的优缺点
- 优点:
1、降低了系统耦合度。
2、新的命令可以很容易添加到系统中去。 - 缺点:
使用命令模式可能会导致某些系统有过多的具体命令类。
2.3 解释器模式
(1)概念
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
**何时使用:**如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
**如何解决:**构建语法树,定义终结符与非终结符。
(2)适用场景
1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
2、一些重复出现的问题可以用一种简单的语言来进行表达。
3、一个简单语法需要解释的场景。
(3)代码示例
我们将创建一个接口 Expression 和实现了 Expression 接口的实体类。定义作为上下文中主要解释器的 TerminalExpression 类。其他的类 OrExpression、AndExpression 用于创建组合式表达式。
InterpreterPatternDemo,我们的演示类使用 Expression 类创建规则和演示表达式的解析。
创建一个表达式接口。
package com.alibaba.design.interpreterpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-10:10 */ public interface Expression { public boolean interpret(String context); }
创建实现了上述接口的实体类。
- TerminalExpression
package com.alibaba.design.interpreterpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-10:10 */ 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; } }
- OrExpression
package com.alibaba.design.interpreterpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-10:11 */ 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); } }
- AndExpression
package com.alibaba.design.interpreterpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-10:12 */ 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); } }
客户端测试类
package com.alibaba.design.interpreterpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/1-10:13 */ 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)该模式在源码中的体现
JDK中的应用:
类Pattern:正则表达式就是一个解释器模式的很好体现,因为正则表达式本身就是一个语法的封装。
在Spring中的应用
org.springframework.expression.EpressionParser类EpressionParser它也封装了字符串表达式的语法,其内部采用的也是解释器模式
(5)解释器模式的优缺点
- 优点:
1、可扩展性比较好,灵活。
2、增加了新的解释表达式的方式。
3、易于实现简单文法。 - 缺点:
1、可利用场景比较少。
2、对于复杂的文法比较难维护。
3、解释器模式会引起类膨胀。
4、解释器模式采用递归调用方法。
**注意事项:**可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。