天天看點

設計模式-職責鍊模式(上)

1、模闆模式、政策模式、職責鍊模式。這三種模式具有相同的作用:複用和擴充,在實際的項目開發中比較常用,特别是架構開發中,我們可以利用它們來提供架構的擴充點,能夠讓架構的使用者在不修改架構源碼的情況下,基于擴充點定制化架構的功能。

2、什麼是指責鍊模式:

設計模式-職責鍊模式(上)

       翻譯成中文就是:将請求的發送和接收解耦,讓多個接收對象都有機會處理這個請求。将這些接收對象串成一條鍊,并沿着這條鍊傳遞這個請求,直到鍊上的某個接收對象能夠處理它為止。3、職責鍊模式的模闆代碼:(這裡既用到了模闆模式也用到了職責鍊模式)

public abstract class Handler {
  protected Handler successor = null;

  public void setSuccessor(Handler successor) {
    this.successor = successor;
  }

  public final void handle() {
    boolean handled = doHandle();
    if (successor != null && !handled) {
      successor.handle();
    }
  }

  protected abstract boolean doHandle();
}

public class HandlerA extends Handler {
  @Override
  protected boolean doHandle() {
    boolean handled = false;
    //...
    return handled;
  }
}

public class HandlerB extends Handler {
  @Override
  protected boolean doHandle() {
    boolean handled = false;
    //...
    return handled;
  }
}


public class HandlerChain {
  private Handler head = null;
  private Handler tail = null;

  public void addHandler(Handler handler) {
    handler.setSuccessor(null);

    if (head == null) {
      head = handler;
      tail = handler;
      return;
    }

    tail.setSuccessor(handler);
    tail = handler;
  }

  public void handle() {
    if (head != null) {
      head.handle();
    }
  }
}

// 使用舉例
public class Application {
  public static void main(String[] args) {
    HandlerChain chain = new HandlerChain();
    chain.addHandler(new HandlerA());
    chain.addHandler(new HandlerB());
    chain.handle();
  }
}
           

4、 第二種實作方式,代碼如下所示。這種實作方式更加簡單。HandlerChain 類用數組而非連結清單來儲存所有的處理器,并且需要在 HandlerChain 的 handle() 函數中,依次調用每個處理器的 handle() 函數。

/**
 * 處理器接口
 * @author PengMvc
 *
 */
public interface Ihandle {
	public Boolean handle();
}

/**
 * Ahandle類
 * @author PengMvc
 *
 */
public class Ahandle implements Ihandle{

	@Override
	public Boolean handle() {
		System.out.println("我是A處理器");
		// 省略A處理器的具體處理邏輯
		return true;
	}

}

/**
 * Bhandle類
 * @author PengMvc
 *
 */
public class Bhandle implements Ihandle{

	@Override
	public Boolean handle() {
		System.out.println("我是B處理器");
		// 省略B處理器的具體處理邏輯
		return true;
	}

}

/**
 * 處理器鍊
 * 隻要一個沒有處理成功,那麼整個處理器就沒有成功
 * @author PengMvc
 *
 */
public class HandleChain {
	
	List<Ihandle> handleList=new ArrayList();

	// 注冊處理器
	public void addHandle(Ihandle handle) {
		this.handleList.add(handle);
	}
    
	// 判斷是否處理成功
	public Boolean doHandle() {
	  for(Ihandle handle :handleList) {
			if(!handle.handle()) {
				return false;
			}
		}
		return true;
	}
	
}

/**
 * 處理器鍊,無論如何所有的處理器都會執行一遍
 * 最後根據處理的結果,确定是否處理成功
 * @author PengMvc
 *
 */
public class HandleChain2 {
	
	List<Ihandle> handleList = new ArrayList();
	
	// 各個處理器的結果集合
	List<Boolean> ifSuccessList = new ArrayList();

	// 注冊處理器
	public void addHandle(Ihandle handle) {
		this.handleList.add(handle);
	}
    
	// 判斷是否處理成功
	public Boolean doHandle() {
		Boolean ifHandle =  true;
		for(Ihandle handle :handleList) {
			ifSuccessList.add(handle.handle());
		}
	List<Boolean> filterList=ifSuccessList.stream().
                    filter(flag->flag.equals(false)).collect(Collectors.toList());
	ifHandle = (filterList !=null && filterList.size()==0) ? true : false;
	    
	return ifHandle ;
	}
}

/**
 * 測試類
 * @author PengMvc
 *
 */
public class HandleTest {
	public static void main(String[] args) {
		HandleChain handleChain = new HandleChain();
		
		// handleChain注冊需要處理的處理器
		handleChain.addHandle(new Ahandle());
		handleChain.addHandle(new Bhandle());
		
		// 進行處理
		String ifsuccess=handleChain.doHandle() ? "所用處理器處理成功":"處理失敗";
		System.out.println(ifsuccess);
	}
}



           

5、職責鍊模式的應用場景:

利用職責鍊模式可以靈活擴充敏感詞過濾架構:代碼如下

public interface SensitiveWordFilter {
  boolean doFilter(Content content);
}

public class SexyWordFilter implements SensitiveWordFilter {
  @Override
  public boolean doFilter(Content content) {
    boolean legal = true;
    //...
    return legal;
  }
}

// PoliticalWordFilter、AdsWordFilter類代碼結構與SexyWordFilter類似

public class SensitiveWordFilterChain {
  private List<SensitiveWordFilter> filters = new ArrayList<>();

  public void addFilter(SensitiveWordFilter filter) {
    this.filters.add(filter);
  }

  // return true if content doesn't contain sensitive words.
  public boolean filter(Content content) {
    for (SensitiveWordFilter filter : filters) {
      if (!filter.doFilter(content)) {
        return false;
      }
    }
    return true;
  }
}

public class ApplicationDemo {
  public static void main(String[] args) {
    SensitiveWordFilterChain filterChain = new SensitiveWordFilterChain();
    filterChain.addFilter(new AdsWordFilter());
    filterChain.addFilter(new SexyWordFilter());
    filterChain.addFilter(new PoliticalWordFilter());

    boolean legal = filterChain.filter(new Content());
    if (!legal) {
      // 不發表
    } else {
      // 發表
    }
  }
}
           

       看了上面的實作,你可能會說,我像下面這樣也可以實作敏感詞過濾功能,而且代碼更加簡單,為什麼非要使用職責鍊模式呢?這是不是過度設計呢?以下是不是很好的實作:

public class SensitiveWordFilter {
  // return true if content doesn't contain sensitive words.
  public boolean filter(Content content) {
    if (!filterSexyWord(content)) {
      return false;
    }

    if (!filterAdsWord(content)) {
      return false;
    }

    if (!filterPoliticalWord(content)) {
      return false;
    }

    return true;
  }

  private boolean filterSexyWord(Content content) {
    //....
  }

  private boolean filterAdsWord(Content content) {
    //...
  }

  private boolean filterPoliticalWord(Content content) {
    //...
  }
}
           

5.1、使用職責鍊模式的好處:

5.1.1、首先,我們來看,職責鍊模式如何應對代碼的複雜性。

      将大塊代碼邏輯拆分成函數,将大類拆分成小類,是應對代碼複雜性的常用方法。應用職責鍊模式,我們把各個敏感詞過濾函數繼續拆分出來,設計成獨立的類,進一步簡化了 SensitiveWordFilter 類,讓 SensitiveWordFilter 類的代碼不會過多,過複雜。

5.1.2、我們再來看,職責鍊模式如何讓代碼滿足開閉原則,提高代碼的擴充性。

       當我們要擴充新的過濾算法的時候,比如,我們還需要過濾特殊符号,按照非職責鍊模式的代碼實作方式,我們需要修改 SensitiveWordFilter 的代碼,違反開閉原則。不過,這樣的修改還算比較集中,也是可以接受的。而職責鍊模式的實作方式更加優雅,隻需要新添加一個 Filter 類,并且通過 addFilter() 函數将它添加到 FilterChain 中即可,其他代碼完全不需要修改。

       不過,你可能會說,即便使用職責鍊模式來實作,當添加新的過濾算法的時候,還是要修改用戶端代碼(ApplicationDemo),這樣做也沒有完全符合開閉原則。

       實際上,細化一下的話,我們可以把上面的代碼分成兩類:架構代碼和用戶端代碼。其中,ApplicationDemo 屬于用戶端代碼,也就是使用架構的代碼。除 ApplicationDemo 之外的代碼屬于敏感詞過濾架構代碼。

       假設敏感詞過濾架構并不是我們開發維護的,而是我們引入的一個第三方架構,我們要擴充一個新的過濾算法,不可能直接去修改架構的源碼。這個時候,利用職責鍊模式就能達到開篇所說的,在不修改架構源碼的情況下,基于職責鍊模式提供的擴充點,來擴充新的功能。換句話說,我們在架構這個代碼範圍内實作了開閉原則。

       除此之外,利用職責鍊模式相對于不用職責鍊的實作方式,還有一個好處,那就是配置過濾算法更加靈活,可以隻選擇使用某幾個過濾算法,譬如我們可以通過properties配置我們的filter,我簡單的實作如下可以參考,我們加了properties,很多項目都有配置中心,那麼我們可以在配置中心裡靈活的配置我們想要的filter,這樣代碼更加符合開閉原則,代碼如下:

/**
 * handle用戶端
 * @author PengMvc
 *
 */
public class HandleApplication {
	
	// autowire handle
    private static final HandleChain handleChain = new HandleChain();
    
    // when application started,all handles already load to chain.
    static {
    	FileInputStream fileInputStream;
		try {
			Properties properties = new Properties();
			fileInputStream = new FileInputStream(new File("src/resource/handlebean.properties"));
			properties.load(fileInputStream);
			Enumeration<Object> keys = properties.keys();
			while(keys.hasMoreElements()) {
				// key
				String key = keys.nextElement().toString();
				
				// get classPath
				String classPath = properties.getProperty(key);
				
				// get bean 
				Ihandle instance = (Ihandle) Class.forName(classPath).newInstance();
				
				// register handle
				handleChain.addHandle(instance);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
	
	public static void main(String[] args) throws Exception {
		// to handle
		String ifsuccess=handleChain.doHandle() ? "所用處理器處理成功":"處理失敗";
		System.out.println(ifsuccess);
	}
	
  
}
           

6、總結:

       在職責鍊模式中,多個處理器依次處理同一個請求。一個請求先經過 A 處理器處理,然後再把請求傳遞給 B 處理器,B 處理器處理完後再傳遞給 C 處理器,以此類推,形成一個鍊條。鍊條上的每個處理器各自承擔各自的處理職責,是以叫作職責鍊模式。

       在 GoF 的定義中,一旦某個處理器能處理這個請求,就不會繼續将請求傳遞給後續的處理器了。當然,在實際的開發中,也存在對這個模式的變體,那就是請求不會中途終止傳遞,而是會被所有的處理器都處理一遍。

       職責鍊模式有兩種常用的實作。一種是使用連結清單來存儲處理器,另一種是使用數組來存儲處理器,後面一種實作方式更加簡單。

繼續閱讀