責任鏈模式
用過tomcat或者springMVC的人應該都對過濾器不陌生吧,過濾器的實現就是責任鏈模式。
且看如何一步一步推到。話不多說,直接上代碼。
//這段代碼是實現將罵人詞彙過濾:
public class Main {
public static void main(String[] args) {
Msg msg = new Msg();
msg.setMsg("djsiaohfioja shit dsjakljf wtf is");
String r= msg.getMsg();
r = r.replace("shit","ssss");
msg.setMsg(r);
r = r.replace("wtf","what");
msg.setMsg(r);
System.out.println(msg);
}
}
class Msg{
String msg;
@Override
public String toString() {
return "Msg{" +
"msg='" + msg + '\'' +
'}';
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
以上是兩個過濾的方法,當有更多的過濾方法的話,如果來一個就往源碼中添加一個,那是極其麻煩的,而且不易拓展。
可以這樣修改:
將過濾操作抽象成一個接口
interface Filter{
void doFilter(Msg m);
}
定義兩個過濾器:
class ShitFilter implements Filter{
@Override
public void doFilter(Msg m) {
String r = m.getMsg();
r = r.replace("shit","ssss");
m.setMsg(r);
}
}
class WtfFilter implements Filter{
@Override
public void doFilter(Msg m) {
String r = m.getMsg();
r = r.replace("wtf","what");
m.setMsg(r);
}
}
現在我們只需要在main中:
public class Main {
public static void main(String[] args) {
Msg msg = new Msg();
msg.setMsg("djsiaohfioja shit dsjakljf wtf is");
new ShitFilter().doFilter(msg);
new WtfFilter().doFilter(msg);
System.out.println(msg);
}
}
這樣就實現了過濾的功能,但是此時如果有新的過濾器,還是要添加代碼。我們可以將過濾器添加到一個List集合中,這樣做是爲了之後可以將過濾器定義到配置文件中,而向list中添加元素時,讀取配置文件添加,這樣就避免了修改源碼:
public class Main {
public static void main(String[] args) {
List<Filter> filters = new ArrayList<>();
Msg msg = new Msg();
msg.setMsg("djsiaohfioja shit dsjakljf wtf is");
filters.add(new ShitFilter());
filters.add(new WtfFilter());
for (Filter filter : filters) {
filter.doFilter(msg);
}
System.out.println(msg);
}
}
架構類似於:
此時只是向list中添加,然後遍歷執行,其實並不是一條鏈,只是挨個執行而已,我們將過濾器串成一條鏈,
定義類,並將list封裝到其中,方法包括向鏈中添加過濾器(add)、從頭開始運行這條鏈(doFilter)、刪除過濾器…:
class FilterChain{
List<Filter> filters = new ArrayList<>();
public void add(Filter filter)
{
filters.add(filter);
}
public void doFilter(Msg msg)
{
for (Filter filter : filters) {
filter.doFilter(msg);
}
}
//刪除filter等等一系列方法...
}
main中修改爲:
public class Main {
public static void main(String[] args) {
Msg msg = new Msg();
msg.setMsg("djsiaohfioja shit dsjakljf wtf is");
FilterChain filters = new FilterChain();
filters.add(new ShitFilter());
filters.add(new WtfFilter());
filters.doFilter(msg);
System.out.println(msg);
}
}
看到這裏你或許有疑問,這有什麼區別呢,不就是把list裝到了FilterChain裏了嗎,沒有區別啊。
區別在什麼地方呢?
其實區別很大,當我們有兩個FilterChain時,其實是可以將兩個鏈條連在一起的。
在實現之前先做一個小小的修改,因爲每次filters都要add,所以我們將這裏改爲鏈式編程模式,將FilterChain中的add方法修改,返回FilterChain對象,即返回當前對象本身。
public FilterChain add(Filter filter)
{
filters.add(filter);
return this;
}
之前我們如果要添加過濾器:
filters.add(new ShitFilter());
filters.add(new WtfFilter());
現在我們只需:
filters.add(new ShitFilter()).add(new WtfFilter());
是不是感覺簡便了許多(o゚v゚)ノ
現在當我們有兩條filterchiain時,該怎麼連起來呢?首先我們模擬一下多鏈條:
定義
class FuckFilter implements Filter{
@Override
public void doFilter(Msg m) {
String r = m.getMsg();
r = r.replace("fuck","****");
m.setMsg(r);
}
}
class BitchFilter implements Filter{
@Override
public void doFilter(Msg m) {
String r = m.getMsg();
r = r.replace("bitch","bbbbb");
m.setMsg(r);
}
}
定義兩條鏈:
FilterChain filters = new FilterChain();
filters.add(new ShitFilter())
.add(new WtfFilter());
FilterChain filters2 = new FilterChain();
filters.add(new FuckFilter())
.add(new BitchFilter());
filters.doFilter(msg);
filters2.doFilter(msg);
filters與filters2怎麼合併呢?
很簡單,只需讓FilterChain也實現Filter接口:
class FilterChain implements Filter{
List<Filter> filters = new ArrayList<>();
public FilterChain add(Filter filter)
{
filters.add(filter);
return this;
}
public void doFilter(Msg msg)
{
for (Filter filter : filters) {
filter.doFilter(msg);
}
}
//刪除filter等等一系列方法...
}
現在我們只需要這樣:
FilterChain filters = new FilterChain();
filters.add(new ShitFilter())
.add(new WtfFilter());
FilterChain filters2 = new FilterChain();
filters.add(new FuckFilter())
.add(new BitchFilter());
filters.add(filters2);
filters.add(filters2);
這樣就實現了兩個鏈條連在了一起。
那麼它是怎麼執行的呢?
這種調用類似於遞歸,當我們運行filters.doFilter(msg)
時,會執行:
//這裏是filters的循環,會調用filters2的doFilter
for (Filter filter : filters) {
filter.doFilter(msg);
}
因爲我們的filters2也實現了Filter接口,所以當執行到filters2時,調用filters2的doFilter(msg)方法,這樣就會執行filters2中的過濾器:
//這裏是filters2的循環,被filters調用
for (Filter filter : filters) {
filter.doFilter(msg);
}
這樣我們不論寫多少過濾器,都可以鏈成一條
我們再進一步改進,上面的過濾器都是一下子全部執行完,但是現在需要的是,思考這種場景:當執行到一個過濾器時,如果要是發現異常,就不要再往下再執行了,該怎麼實現呢?
我們只需要將Filter中的doFilter方法返回值改爲boolean類型:
interface Filter{
boolean doFilter(Msg m);
}
class ShitFilter implements Filter{
@Override
public boolean doFilter(Msg m) {
String r = m.getMsg();
r = r.replace("shit","ssss");
m.setMsg(r);
//這裏可以做判斷,如果不想讓他往下執行,返回false.
return true;
}
}
//其他過濾器一樣
然後我們修改FilterChain中的doFilter方法:
public boolean doFilter(Msg msg)
{
for (Filter filter : filters) {
if (!filter.doFilter(msg)) return false;
}
return true;
}
當其中一環返回false,那就返回false,不往下執行了。
思考題:servlet中的過濾器是這樣的:
如何實現過濾器,使其處理request時是f1,f2,f3,f4的順序,處理response時的順序是f4,f3,f2,f1?
答案:設計模式之責任鏈模式思考題(servlet中filter過濾器是如何實現的?)