設計模式 之 責任鏈模式學習實踐


一、概述

責任鏈模式是行為模式的一種,它將需要觸發的Handler組成一條鏈,
發送者將請求發給鏈的第一個接收者,並且沿着這條鏈傳遞,直到有一個Handler來處理它或者直到最后也沒有對象處理而留在鏈末尾端;
責任連模式的重點是在鏈上,由一條鏈去處理相似的請求,在鏈中決定誰來處理這個請求。

責任鏈模式的角色:

  • 抽象處理者角色(Handler):該角色對請求進行抽象處理,並定義一個方法和返回下一個處理的引用。
  • 具體處理者(Concrete Handler)角色:該角色接到請求后,可以選擇處理掉,或則將請求傳給下一個處理則。
    由於具體處理者持有對下一個處理者的引用,因此,如果需要,可以訪問下一個處理者。
  • 客戶端:事件的發起者。

二、實現舉例

以對字符串進行簡單過濾處理為例:

  • 定義一個接口,包含一個處理方法
public interface Filter {
	String doFilter(String str);
}

  • 不同的處理過濾器,實現接口重寫方法

如:

// FaceFilter.java
public class FaceFilter implements Filter {

	@Override
	public String doFilter(String str) {
		return str.replace(":)", "^V^");
	}
}
// SesitiveFilter.java
public class SesitiveFilter implements Filter {

	@Override
	public String doFilter(String str) {
		//process the sensitive words
		String r = str.replace("敏感", "和諧");
		return r;
	}

}

以及其他具體的filter。

  • FilterChain同樣實現Filter,重寫doFilter方法。以存放並依次執行各filter的處理
import java.util.ArrayList;
import java.util.List;

public class FilterChain implements Filter {
	List<Filter> filters = new ArrayList<Filter>();
	
	public FilterChain addFilter(Filter f) {
		this.filters.add(f);
		return this;
	}
	// 各filter依次處理
	public String doFilter(String str) {
		String r = str;
		for(Filter f: filters) {
			r = f.doFilter(r);
		}
		return r;
	}
}
  • 消息處理類
public class MsgProcessor {
	private String msg;
	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
	
	/**
	 * 處理方式0:過濾項全部放在一起處理   X
	 * 不利於靈活的修改和擴展。
	 * @return
	 */
	public String process_0() {
        // 先處理帶尖括號的信息  
        String r=msg.replace('<', '[')  
                    .replace('>', ']');  
          
        // 處理敏感字眼  
        r=r.replace("敏感", "和諧");  
        return r;  
	}
	
	/** 處理方式1 BEGIN**/
	// 配置需要的filter
	Filter[] filters = { new SesitiveFilter(), new FaceFilter()};
	// 按照預先配置的filter一次處理。
	public String process_1(){
        String r=msg;  
        // 處理腳本語句和敏感字眼  
        for(Filter f:filters){  
            r=f.doFilter(r);  
        }  
        return r;  
	}
	/** 處理方式1 END**/
	
	/** 處理方式2 BEGIN**/
	// 使用一個處理鏈
	FilterChain fc;
	public FilterChain getFc() {
		return fc;
	}
	public void setFc(FilterChain fc) {
		this.fc = fc;
	}
	public String process() {
		return fc.doFilter(msg);
	}
	/** 處理方式2 END**/
	
}
  • 測試
public class Main {

	public static void main(String[] args) {
		String msg = "<script>, 敏感信息:)";
		MsgProcessor mp = new MsgProcessor();
		mp.setMsg(msg);
		
		
		FilterChain fc = new FilterChain();
		fc.addFilter(new FaceFilter())
		  .addFilter(new SesitiveFilter());

		mp.setFc(fc);
		
		String result = mp.process();
		System.out.println(result);
	}

}

輸出結果:
<script>, 和諧信息^V^

三、典型應用

Web

Servlet 和 Filter
以聲明的方式插入到Http請求響應的處理過程,用於攔截請求和響應.

Netty

ChannelPieline 和 ChannelHandler
Channel的數據管道抽象為ChannelPieline,消息在ChannelPieline中流動傳遞,ChannelPieline持有I/O事件攔截器ChannelHandler的鏈表.可以通過新增和刪除Handler來實現不同的業務邏輯定制.

    public void initServer() {
        unStop();
        logger.info("Attempt to init " + (ConfigUtil.IS_INNER_PROXY ? "UDP" : "TCP") + " Server!" + logTunnelInfo);
        server = ConfigUtil.IS_INNER_PROXY ? new ProxyUdpServer() : new ProxyTcpServer();
        server.setSelfPort(Integer.parseInt(channelConfig.getChannelTheEndPort()));
        server.setChannelInitializer(new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel ch) throws Exception {
                ChannelPipeline channelPipeline = ch.pipeline();
                channelPipeline.addLast(ConfigUtil.IS_INNER_PROXY ? new UdpChecker(channelConfig, corral) : new TcpChecker(channelConfig, corral));
                channelPipeline.addLast(ConfigUtil.IS_INNER_PROXY ? new DatagramPacketDecoder() : new MsgDecoder());
                channelPipeline.addLast(new ProxyServerChannelHandler());
            }
        });
        server.start();
        try {
            server.bind().addListener(future -> applicationListenerManager.publishEvent(ConfigUtil.IS_INNER_PROXY ? InnerProxyToOutterProxy.class : OutterProxyToInnerProxy.class, new EnhancedParams(channelConfig, corral)));
        } catch (Exception e) {
            logger.error("Proxy " + (ConfigUtil.IS_INNER_PROXY ? "UDP" : "TCP") + " Server start failed!" + logTunnelInfo, e);
        }
    }

四、簡單實踐

場景描述: 某一鍵配置場景,需要將當前系統業務參數配置導出到Excel。
業務參數按照按不同類別寫入不同的sheet頁。可以把文件流作為輸入,寫不同sheet頁作為多個Handler,后續新增配置參數或sheet頁都可以很方便的修改。

  • 接口
import org.apache.poi.ss.usermodel.Workbook;
import com.tdtech.eplatform.gatekeeper.util.BusinessException;

public interface ExcelWriteHandler {
	Workbook doWriteExcel(Workbook wb) throws BusinessException;
}
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.ss.usermodel.Workbook;
import com.tdtech.eplatform.gatekeeper.util.BusinessException;

public class ExcelWriteHandlerChain implements ExcelWriteHandler{

	List<ExcelWriteHandler>  excelWriters = new ArrayList<ExcelWriteHandler>();
	
	 public ExcelWriteHandlerChain addHandler(ExcelWriteHandler eWriteHandler) {
		 this.excelWriters.add(eWriteHandler);
		 return this;
	}
	
	@Override
	public Workbook doWriteExcel(Workbook workbook) throws BusinessException {
		Workbook wb = workbook;
		for(ExcelWriteHandler excelWriter: excelWriters) {
			wb = excelWriter.doWriteExcel(wb);
		}
		return wb;
	}
}

  • 具體的某一個處理類
import java.util.List;

import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.tdtech.eplatform.gatekeeper.common.IGetAndSetConfig;
import com.tdtech.eplatform.gatekeeper.config.entity.ChannelConfig;
import com.tdtech.eplatform.gatekeeper.constant.IConfigFileSet;
import com.tdtech.eplatform.gatekeeper.util.BusinessException;
import com.tdtech.eplatform.gatekeeper.util.FileUtil;

@Service
public class ExcelWriteTunnelHandler implements ExcelWriteHandler{
	@Autowired
	private IGetAndSetConfig getconfp;
	
	@Override
	public Workbook doWriteExcel(Workbook wb) throws BusinessException {
		Sheet sheetTunnel = wb.getSheet(IConfigFileSet.sheetNameofTunnnel);
		if (sheetTunnel == null) {
			throw new BusinessException("1", "寫配置文件失敗");
		}
		
		List<ChannelConfig> allChannelConfig = getconfp.getAllChannelConfig();
		CellStyle style = FileUtil.getCellStyle(wb);
		int rowNum = 0;
		for (ChannelConfig channelConfig : allChannelConfig) {
			Row rows = sheetTunnel.createRow(++rowNum);
			FileUtil.setCellValue(rows, 0, style, channelConfig.getChannelName());
			FileUtil.setCellValue(rows, 1, style, channelConfig.getOppositeEndForwardVirtualIP());
			FileUtil.setCellValue(rows, 2, style, channelConfig.getOppositeEndOppositeVirtualIP());
			FileUtil.setCellValue(rows, 3, style, String.valueOf(channelConfig.getChannelTheEndPort()));
			FileUtil.setCellValue(rows, 4, style, String.valueOf(channelConfig.getChannelToEndPort()));
		}
		return wb;
	}

}
	private boolean writeEasyconfigExcel() throws BusinessException {
		boolean rst = false;
		// 讀空模板文件
		ExcelReader eReader = new ExcelReader(IConfigFileSet.exportEasyconfBaseFile,IConfigFileSet.sheetNameofListenIPMapping);
		if(!eReader.isSheetExist()){
			logger.error("sheetNameofGate not exists.");
			throw new BusinessException("1", "獲取一鍵配置文件失敗。");
		}
		ExcelWriteHandlerChain handlers = new ExcelWriteHandlerChain();
		// 寫網閘參數/通道參數/監聽參數
		handlers.addHandler(eWriteForGate)
				.addHandler(eWriteForTunnel)
				.addHandler(eWriteForListener);
		Workbook wb = handlers.doWriteExcel(eReader.getWorkBook());
		rst = FileUtil.writeExcel(wb,IConfigFileSet.exportEasyconfFilePath);
		return rst;
	}

五、體會

所謂責任,就是一個Handler做某一種處理。各個Handler之間低耦合,易於擴展。
所謂鏈,就是各個Handler依次對請求做相應處理的一種組織形式。
請求者與處理者是解耦的。請求者只需要發出請求,鏈條中的各個具體處理者負責處理自己的那部分。
簡言之就是:分離職責,動態組合。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM