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功能會將用戶講的語音進行翻譯,從而解釋使用者的意思,並根據使用者的請求,反饋相應的信息,這就是解釋器模式在手機中的實際應用。
在日常中使用的計算器,根據用戶輸入的數字和運算符號,對表達式進行解析,最終得出計算結果,所謂解析,其實就是對用戶輸入的字符進行解釋,分析出哪些是數字,哪些是運算符,是否有運算符優先級的處理等,這些都是解釋器模式需要做的事情。