DesignPattern : Chain-Of-Responsibility

1. Application Scenario:

    1) We want to build an BBS and enable user to post their comments.

        How can we filter the sensitive messages that user posted?

    2) In chatting room, our message are filtered and tagged as " Davy Says: *** "

        " Davy Says " has been added into message.

 

2. Traditional Approach:

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

public class MsgProcessor
{
	private String msg;

	public String process()
	{
		// process html <> tag
		String result = msg.replaceAll("<", "{").replaceAll(">", "}");

		// process sensitive words
		result = result.replaceAll("Sensitive", "Non-sensitive").replaceAll(
				"Bad", "Good");

		return result;
	}

	public String getMsg()
	{
		return msg;
	}

	public void setMsg(String msg)
	{
		this.msg = msg;
	}

}

 

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

import org.junit.Test;

public class AppTest
{
	private String message = "Sensitive, <script>, Hello World, Forbidden, Bad";

	@Test
	public void test()
	{
		MsgProcessor processor = new MsgProcessor();
		processor.setMsg(message);
		String result = processor.process();
		System.out.println(result);
	}
}

  

Non-sensitive, {script}, Hello World, Forbidden, Good

    Comments:

 

        1) As we can see, the processor is tightly coupled with the message itself and the rules.

        2) Every time we want to add some new rules, we have to change the code inside the method process().

        3) How can we use another more flexible and low-coupled approach to realize this?

            --> We can make several filters, each filter focus on its specific rules.

            --> Every time we want to add or delete some rules, we just add or delete the fiters.

3. Chain Of Responsibility Approach

    1) Filter Interface

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

public interface Filter
{
	String doFiler(String msg);
}

    2) HtmlFilter

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

public class HtmlFilter implements Filter
{

	public String doFiler(String msg)
	{
		return msg.replaceAll("<", "{").replaceAll(">", "}");
	}

}

    3) SensitiveFilter

 

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

public class SensitiveFilter implements Filter
{

	public String doFiler(String msg)
	{
		return msg.replaceAll("Sensitive", "Non-sensitive").replaceAll("Bad",
				"Good");
	}

}

    4) MsgProcessor

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

public class MsgProcessor
{
	private String msg;
	private List<Filter> filterList;

	public MsgProcessor()
 	{
  		filterList = new ArrayList<Filter>();
		filterList.add(new HtmlFilter());
		filterList.add(new SensitiveFilter());	
 	}
	public MsgProcessor()
	{
	}

	public String process()
	{
  		for (Filter filter : filterList)
  		{
   			msg = filter.doFiler(msg);
  		}
		return msg;	
	}

	public String getMsg()
	{
		return msg;
	}

	public void setMsg(String msg)
	{
		this.msg = msg;
	}

}

   5) Test Case

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

import org.junit.Test;

public class AppTest
{
	private String message = "Sensitive, <script>, Hello World, Forbidden, Bad";
	@Test
	public void test()
	{
		MsgProcessor processor = new MsgProcessor();
		processor.setMsg(message);
		String result = processor.process();
		System.out.println(result);
	}
}

    6) Output

Non-sensitive, {script}, Hello World, Forbidden, Good

    Comments:

    1) Using List we can set the sequence of filter that take effect. That is easy to organize which filter execute first and which filter execute last.

    2) Still there are further requirements:

        1) If there is a filter-chain that already exists. And we want want to add this chain's function into our own filter-chain. How can we easily achieve this?

        2) How can we easily conbine several chains together?

            --> We can get all the filters that in previous filter-chain, and add these filers into our own filter-chain. But that is not convenience enough.

            --> As we can see, a filter-chain performs the same function as a filter.

    1) Added a new class named FilterChain

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

import java.util.ArrayList;
import java.util.List;

public class FilterChain implements Filter
{
	private List<Filter> filterList = new ArrayList<Filter>();

	public String doFiler(String msg)
	{
		for (Filter filter : filterList)
		{
			msg = filter.doFiler(msg);
		}
		return msg;
	}

	public FilterChain addFilter(Filter filter)
	{
		filterList.add(filter);
		return this;
	}

	public void removeFilter(Filter filter)
	{
		filterList.remove(filter);
	}
}

    2) Test case as below

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

import org.junit.Test;

public class AppTest
{
	private String message = "Sensitive, <script>, Hello World, Forbidden, Bad";

	@Test
	public void test()
	{
		FilterChain filterChain = new FilterChain();
		filterChain.addFilter(new HtmlFilter())
				.addFilter(new SensitiveFilter());

		message = filterChain.doFiler(message);
		System.out.println(message);
	}
}

    3) Output is still correct.

 

4. Still there are futher requirements:

    1) We want bi-directional filter which can not only filter the request but also filter the response.

    2) And the request/response message may not be simple String type.

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

public class Request
{
	private String message;

	public String getMessage()
	{
		return message;
	}

	public void setMessage(String message)
	{
		this.message = message;
	}

}
 
package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

public class Response
{
	private String message;

	public String getMessage()
	{
		return message;
	}

	public void setMessage(String message)
	{
		this.message = message;
	}

}
 
package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

public interface Filter
{
	void doFiler(Request request, Response response);
}
 
package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

public class HtmlFilter implements Filter
{

	public void doFiler(Request request, Response response)
	{
		request.setMessage(request.getMessage().replaceAll("<", "{")
				.replaceAll(">", "}"));
	}

}
 
package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

public class SensitiveFilter implements Filter
{

	public void doFiler(Request request, Response response)
	{
		request.setMessage(request.getMessage()
				.replaceAll("Sensitive", "Non-sensitive")
				.replaceAll("Bad", "Good"));
	}

}
 
package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

import java.util.ArrayList;
import java.util.List;

public class FilterChain implements Filter
{
	private List<Filter> filterList = new ArrayList<Filter>();

	public void doFiler(Request request, Response response)
	{
		for (Filter filter : filterList)
		{
			filter.doFiler(request, response);
		}
	}

	public FilterChain addFilter(Filter filter)
	{
		filterList.add(filter);
		return this;
	}

	public void removeFilter(Filter filter)
	{
		filterList.remove(filter);
	}
}

 

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

import org.junit.Test;

public class AppTest
{
	private String requestMsg = "Sensitive, <script>, Hello World, Forbidden, Bad";
	private String responseMsg = "Sensitive, <script>, Hello World, Forbidden, Bad";

	@Test
	public void test()
	{
		FilterChain filterChain = new FilterChain();
		filterChain.addFilter(new HtmlFilter())
				.addFilter(new SensitiveFilter());

		Request request = new Request();
		request.setMessage(requestMsg);
		Response response = new Response();
		response.setMessage(responseMsg);

		filterChain.doFiler(request, response);

		System.out.println(request.getMessage());
		System.out.println(response.getMessage());
	}
}

 

Non-sensitive, {script}, Hello World, Forbidden, Good
Sensitive, <script>, Hello World, Forbidden, Bad

    Comments:

        1) We only move one step futher which encapsulate the type of request and response.

        2) We only process the request and still didn't make any procession to our response object.

        3) Below is the final solution for this problem.

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

public interface Filter
{
	void doFiler(Request request, Response response, FilterChain filterChain);
}

 

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

public class HtmlFilter implements Filter
{

	public void doFiler(Request request, Response response,
			FilterChain filterChain)
	{
		request.setMessage(request.getMessage().replaceAll("<", "{")
				.replaceAll(">", "}").concat("---Processed by HtmlFilter"));

		filterChain.doFiler(request, response, filterChain);

		response.setMessage(response.getMessage().replaceAll("<", "{")
				.replaceAll(">", "}").concat("---Processed by HtmlFilter"));
	}

}

 

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

public class SensitiveFilter implements Filter
{

	public void doFiler(Request request, Response response,
			FilterChain filterChain)
	{
		request.setMessage(request.getMessage()
				.replaceAll("Sensitive", "Non-sensitive")
				.replaceAll("Bad", "Good")
				.concat("---Processed by SensitiveFilter"));

		filterChain.doFiler(request, response, filterChain);

		response.setMessage(response.getMessage()
				.replaceAll("Sensitive", "Non-sensitive")
				.replaceAll("Bad", "Good")
				.concat("---Processed by SensitiveFilter"));
	}

}

 

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

import java.util.ArrayList;
import java.util.List;

public class FilterChain implements Filter
{
	private List<Filter> filterList = new ArrayList<Filter>();
	private int index = 0;

	public void doFiler(Request request, Response response,
			FilterChain filterChain)
	{
		if (index == filterList.size())
		{
			return;
		} else
		{
			Filter filter = filterList.get(index);
			index++;
			filter.doFiler(request, response, filterChain);
		}
	}

	public FilterChain addFilter(Filter filter)
	{
		filterList.add(filter);
		return this;
	}

	public void removeFilter(Filter filter)
	{
		filterList.remove(filter);
	}
}

 

package edu.xmu.designPattern.DesignPattern_ChaniOfResponsibility;

import org.junit.Test;

public class AppTest
{
	private String requestMsg = "Request: Sensitive, <script>, Hello World, Forbidden, Bad";
	private String responseMsg = "Response: Sensitive, <script>, Hello World, Forbidden, Bad";

	@Test
	public void test()
	{
		FilterChain filterChain = new FilterChain();
		filterChain.addFilter(new HtmlFilter())
				.addFilter(new SensitiveFilter());

		Request request = new Request();
		request.setMessage(requestMsg);
		Response response = new Response();
		response.setMessage(responseMsg);

		filterChain.doFiler(request, response, filterChain);

		System.out.println(request.getMessage());
		System.out.println(response.getMessage());
	}
}

 

Request: Non-sensitive, {script}, Hello World, Forbidden, Good---Processed by HtmlFilter---Processed by SensitiveFilter
Response: Non-sensitive, {script}, Hello World, Forbidden, Good---Processed by SensitiveFilter---Processed by HtmlFilter

 

 Extension:

    1) We can take a look at Servlet API. And we can find out interfaces

        -> javax.servlet.FilterChain interface

        -> javax.servlet.Filter interface

       There is a limitation that wa cannot add a filterchain to another as Filter and FilterChain are two different interfaces.

       This design is akward and can be improved as above.

你可能感兴趣的:(DesignPattern : Chain-Of-Responsibility)