深入浅出设计模式(十二):18.职责链模式(Chain of Responsibility)19.状态模式(State)20.解释器模式(Interpreter)

18.职责链模式(Chain of Responsibility)

职责链模式在程序应用中十分广泛,经常使用在公文审批、出差报支等地方,职责链模式的作用就是将对象各自处理的职责分开,虽然职责很多,但最终只有一个职责进行处理。

哪里会使用到职责链模式

比如在玩纸牌,当一个人出牌后,接下来将按照顺序进行询问下一个人是否需要出牌压住上一个人出的牌,如果询问一圈后,无人出牌,则该人继续出牌,然后按照同样规则继续询问,直到有人压住该人的牌,这就好像是一个职责链一样。

再比如“击鼓传花”游戏,在游戏中,一圈人随着击鼓的声音将花不断地往下传,这和职责链也是类似的,谁也不想让花停留在自己的手里,谁都想传递给下一个人,但最终花会落在一个人手里,也就是说虽然前面的人都不想承担这个职责,但是最后还是会有一个人承担受惩罚的职责。

职责链模式的实现原理

这里写图片描述

职责链模式就是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象来处理为止。客户端无需知道其他哪个对象处理其请求,仅需知道该请求会被正确处理,接收者和发送者都没有对方的明确信息。

职责链模式在OA办公中的实际应用

这里写图片描述

职责链模式在Struts的应用

拦截器(Interceptor)是Struts2的核心组成部分。当需要扩展Struts2功能时,
只需提供对应拦截器,并将它配置在Struts2容器中即可。

在Struts2中,最重要的一个类是
org.apache.struts2.dispatcher.org.apache.struts2.dispatcher.FilterDispatcher
在FilterDispatcher过滤器中首先询问ActionMapper是否需要调用某个Action来处理,如果ActionMapper决定需要调用某个Action,FilterDispatcher则把请求的处理交给ActionProxy,ActionProxy通过配置文件struts.xml找到需要调用的Action类,然后ActionProxy创建一个ActionInvocation实例并调用该Action,但在调用之前ActionInvocation会根据配置加载Action相关的所有Interceptor,等Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果result。

所有的Struts2的拦截器都直接或间接实现接口
com.opensymphony.xwork2.interceptor.Interceptor

RolesInterceptor

package chain;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;

public class RolesInterceptor extends AbstractInterceptor {
    // 定义授权角色和不授权角色
    private List<String> allowedRoles = new ArrayList();
    private List<String> disallowedRoles = new ArrayList();

    public void setAllowedRoles(String roles) {
        this.allowedRoles = stringToList(roles);
    }

    public void setDisallowedRoles(String roles) {
        this.disallowedRoles = stringToList(roles);
    }

    // 具体拦截器
    public String intercept(ActionInvocation invocation) throws Exception {
        HttpServletRequest request = ServletActionContext.getRequest();
        HttpServletResponse response = ServletActionContext.getResponse();
        String result = null;
        if (!isAllowed(request, invocation.getAction())) {
            result = handleRejection(invocation, response);
        } else {
            result = invocation.invoke();
        }
        return result;
    }

    // 将字符串转换成List
    protected List<String> stringToList(String val) {
        if (val != null) {
            String[] list = val.split("[ ]*,[ ]*");
            return Arrays.asList(list);
        }
        return Collections.EMPTY_LIST;
    }

    // 判断是否有某一方法的权限
    protected boolean isAllowed(HttpServletRequest request, Object action) {
        if (this.allowedRoles.size() > 0) {
            boolean result = false;
            for (String role : this.allowedRoles) {
                if (request.isUserInRole(role)) {
                    result = true;
                }
            }
            return result;
        }
        if (this.disallowedRoles.size() > 0) {
            for (String role : this.disallowedRoles) {
                if (request.isUserInRole(role)) {
                    return false;
                }
            }
        }
        return true;
    }

    // 发送错误信息
    protected String handleRejection(ActionInvocation invocation,
            HttpServletResponse response) throws Exception {
        response.sendError(403);
        return null;
    }
}

FileUploadInterceptor

package org.apache.struts2.interceptor;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.ValidationAware;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.util.PatternMatcher;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;

public class FileUploadInterceptor extends AbstractInterceptor {
    private static final long serialVersionUID = -4764627478894962478L;
    protected static final Logger LOG = LoggerFactory
            .getLogger(FileUploadInterceptor.class);
    protected Long maximumSize;
    protected Set<String> allowedTypesSet = Collections.emptySet();
    protected Set<String> allowedExtensionsSet = Collections.emptySet();
    private PatternMatcher matcher;
    private Container container;

    @Inject
    public void setMatcher(PatternMatcher matcher) {
        this.matcher = matcher;
    }

    @Inject
    public void setContainer(Container container) {
        this.container = container;
    }

    public void setAllowedExtensions(String allowedExtensions) {
        this.allowedExtensionsSet = TextParseUtil
                .commaDelimitedStringToSet(allowedExtensions);
    }

    public void setAllowedTypes(String allowedTypes) {
        this.allowedTypesSet = TextParseUtil
                .commaDelimitedStringToSet(allowedTypes);
    }

    public void setMaximumSize(Long maximumSize) {
        this.maximumSize = maximumSize;
    }

    // 具体拦截器实现
    public String intercept(ActionInvocation invocation) throws Exception {
        ActionContext ac = invocation.getInvocationContext();

        HttpServletRequest request = (HttpServletRequest) ac
                .get("com.opensymphony.xwork2.dispatcher.HttpServletRequest");
        if (!(request instanceof MultiPartRequestWrapper)) {
            if (LOG.isDebugEnabled()) {
                ActionProxy proxy = invocation.getProxy();
                LOG.debug(
                        getTextMessage(
                                "struts.messages.bypass.request",
                                new String[] { proxy.getNamespace(),
                                        proxy.getActionName() }), new String[0]);
            }
            // 反射
            return invocation.invoke();
        }
        ValidationAware validation = null;

        Object action = invocation.getAction();
        if ((action instanceof ValidationAware)) {
            validation = (ValidationAware) action;
        }
        // 装饰器模式的应用
        MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) request;
        // 判断是否有错误
        if (multiWrapper.hasErrors()) {
            for (String error : multiWrapper.getErrors()) {
                if (validation != null) {
                    validation.addActionError(error);
                }
            }
        }
        // 绑定文件
        Enumeration fileParameterNames = multiWrapper.getFileParameterNames();
        while ((fileParameterNames != null)
                && (fileParameterNames.hasMoreElements())) {
            // 获取参数
            String inputName = (String) fileParameterNames.nextElement();
            // 获取文件类型
            String[] contentType = multiWrapper.getContentTypes(inputName);
            // 判断文件类型是否为空
            if (isNonEmpty(contentType)) {
                // 获取输入的文件名
                String[] fileName = multiWrapper.getFileNames(inputName);
                if (isNonEmpty(fileName)) {
                    // 获取要上传或下载的文件
                    File[] files = multiWrapper.getFiles(inputName);
                    if ((files != null) && (files.length > 0)) {
                        List<File> acceptedFiles = new ArrayList(files.length);
                        List<String> acceptedContentTypes = new ArrayList(
                                files.length);
                        List<String> acceptedFileNames = new ArrayList(
                                files.length);
                        String contentTypeName = inputName + "ContentType";
                        String fileNameName = inputName + "FileName";
                        // 添加文件
                        for (int index = 0; index < files.length; index++) {
                            if (acceptFile(action, files[index],
                                    fileName[index], contentType[index],
                                    inputName, validation)) {
                                acceptedFiles.add(files[index]);
                                acceptedContentTypes.add(contentType[index]);
                                acceptedFileNames.add(fileName[index]);
                            }
                        }
                        if (!acceptedFiles.isEmpty()) {
                            Map<String, Object> params = ac.getParameters();

                            params.put(inputName, acceptedFiles
                                    .toArray(new File[acceptedFiles.size()]));
                            params.put(contentTypeName, acceptedContentTypes
                                    .toArray(new String[acceptedContentTypes
                                            .size()]));
                            params.put(fileNameName, acceptedFileNames
                                    .toArray(new String[acceptedFileNames
                                            .size()]));
                        }
                    }
                } else if (LOG.isWarnEnabled()) {
                    LOG.warn(
                            getTextMessage(action,
                                    "struts.messages.invalid.file",
                                    new String[] { inputName }), new String[0]);
                }
            } else if (LOG.isWarnEnabled()) {
                LOG.warn(
                        getTextMessage(action,
                                "struts.messages.invalid.content.type",
                                new String[] { inputName }), new String[0]);
            }
        }
        // 反射
        return invocation.invoke();
    }

    // 接收文件
    protected boolean acceptFile(Object action, File file, String filename,
            String contentType, String inputName, ValidationAware validation) {
        boolean fileIsAcceptable = false;
        if (file == null) {
            String errMsg = getTextMessage(action,
                    "struts.messages.error.uploading",
                    new String[] { inputName });
            if (validation != null) {
                validation.addFieldError(inputName, errMsg);
            }
            if (LOG.isWarnEnabled()) {
                LOG.warn(errMsg, new String[0]);
            }
        } else if ((this.maximumSize != null)
                && (this.maximumSize.longValue() < file.length())) {
            String errMsg = getTextMessage(action,
                    "struts.messages.error.file.too.large",
                    new String[] { inputName, filename, file.getName(),
                            "" + file.length() });
            if (validation != null) {
                validation.addFieldError(inputName, errMsg);
            }
            if (LOG.isWarnEnabled()) {
                LOG.warn(errMsg, new String[0]);
            }
        } else if ((!this.allowedTypesSet.isEmpty())
                && (!containsItem(this.allowedTypesSet, contentType))) {
            String errMsg = getTextMessage(action,
                    "struts.messages.error.content.type.not.allowed",
                    new String[] { inputName, filename, file.getName(),
                            contentType });
            if (validation != null) {
                validation.addFieldError(inputName, errMsg);
            }
            if (LOG.isWarnEnabled()) {
                LOG.warn(errMsg, new String[0]);
            }
        } else if ((!this.allowedExtensionsSet.isEmpty())
                && (!hasAllowedExtension(this.allowedExtensionsSet, filename))) {
            String errMsg = getTextMessage(action,
                    "struts.messages.error.file.extension.not.allowed",
                    new String[] { inputName, filename, file.getName(),
                            contentType });
            if (validation != null) {
                validation.addFieldError(inputName, errMsg);
            }
            if (LOG.isWarnEnabled()) {
                LOG.warn(errMsg, new String[0]);
            }
        } else {
            fileIsAcceptable = true;
        }
        return fileIsAcceptable;
    }

    private boolean hasAllowedExtension(Collection<String> extensionCollection,
            String filename) {
        if (filename == null) {
            return false;
        }
        String lowercaseFilename = filename.toLowerCase();
        for (String extension : extensionCollection) {
            if (lowercaseFilename.endsWith(extension)) {
                return true;
            }
        }
        return false;
    }

    private boolean containsItem(Collection<String> itemCollection, String item) {
        for (String pattern : itemCollection) {
            if (matchesWildcard(pattern, item)) {
                return true;
            }
        }
        return false;
    }

    private boolean matchesWildcard(String pattern, String text) {
        Object o = this.matcher.compilePattern(pattern);
        return this.matcher.match(new HashMap(), text, o);
    }

    private boolean isNonEmpty(Object[] objArray) {
        boolean result = false;
        for (int index = 0; (index < objArray.length) && (!result); index++) {
            if (objArray[index] != null) {
                result = true;
            }
        }
        return result;
    }

    protected String getTextMessage(String messageKey, String[] args) {
        return getTextMessage(this, messageKey, args);
    }

    protected String getTextMessage(Object action, String messageKey,
            String[] args) {
        if ((action instanceof TextProvider)) {
            return ((TextProvider) action).getText(messageKey, args);
        }
        return getTextProvider(action).getText(messageKey, args);
    }

    private TextProvider getTextProvider(Object action) {
        TextProviderFactory tpf = new TextProviderFactory();
        if (this.container != null) {
            this.container.inject(tpf);
        }
        LocaleProvider localeProvider = getLocaleProvider(action);
        return tpf.createInstance(action.getClass(), localeProvider);
    }

    private LocaleProvider getLocaleProvider(Object action) {
        LocaleProvider localeProvider;
        LocaleProvider localeProvider;
        if ((action instanceof LocaleProvider)) {
            localeProvider = (LocaleProvider) action;
        } else {
            localeProvider = (LocaleProvider) this.container
                    .getInstance(LocaleProvider.class);
        }
        return localeProvider;
    }
}

19.状态模式(State)

在上面的职责链模式中,是将条件语句改为多个职责类进行处理,但每个职责类只负责处理一件事情,如果不是自己处理,则自动转给下一个职责类,有一种情况为在转给下一个职责类进行处理前,需要修改当前状态,此时就需要用到状态模式。

哪里会使用到状态模式

比如电梯运行过程中,有着各种各样的状态,比如电梯厢内的检修开关置于“正常”、自动开关置于“运行”位置时,电梯进入自动工作状态,当电梯内有人选择楼层后,电梯可自动关门启动运行;当遇火警信号后,消防员将一楼的消防开关置于运行位置,若此电梯正在上行,则电梯就会在就近的楼层停。

在企业的应用中,公文审批、报支单审批、物资计量也是典型的状态控制方式。

现将上面职责链模式中的出差报支实例进行修改,变成状态模式:
这里写图片描述

状态模式的实现原理

这里写图片描述


20.解释器模式(Interpreter)

解释器模式在软件开发中应用较少,它主要应用于底层的编程语言设计上,因此不太容易理解,本章通过数学公式的转换示例,力图使读者能够由浅入深地理解解释器模式。

哪里会使用到解释器模式

人类很早以前都希望能够通过语音来对电视、洗衣机等电器进行控制,一直以来,这个过程步履艰难,知道iPhone4s的出现,其siri功能会将用户讲的语音进行翻译,从而解释使用者的意思,并根据使用者的请求,反馈相应的信息,这就是解释器模式在手机中的实际应用。

在日常中使用的计算器,根据用户输入的数字和运算符号,对表达式进行解析,最终得出计算结果,所谓解析,其实就是对用户输入的字符进行解释,分析出哪些是数字,哪些是运算符,是否有运算符优先级的处理等,这些都是解释器模式需要做的事情。

解释器模式的实现原理

这里写图片描述

解释器模式在数学公式中的实际应用

这里写图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章