本節要講的並不是傳統意義的責任鏈:爲了不請求發送者與多個請求處理者耦合在一塊兒,將全部請求的處理者經過前一對象記住其下一個對象的引用而連成一條鏈;當有請求發生時,可將請求沿着這條鏈傳遞,直到有對象處理它爲止。也就是說:用戶發起一個請求,以後請求上鍊,若是當前處理者能夠處理該請求,那麼就直接處理;不然當前處理者把請求轉發給下一個處理者。java
本節要講的是責任鏈的一種變形,叫作功能鏈,這種設計模式特別經常使用,隨處可見,用途十分普遍。這種模式是:一個鏈上有多個處理邏輯,一個請求到來會被全部邏輯處理,最後返回最終處理的結果,並非被其中的某一個邏輯處理就結束,而且規則能夠動態添加,git
一、責任鏈的思想github
首先咱們要爲每個規則的執行定義一個接口,由實現類具體執行規則,這些規則也就是針對指定請求的處理邏輯單元。其次咱們要把規則(處理邏輯)關聯起來,最簡單的辦法就是將規則加入到List中,而後循環遍歷執行,固然實際中這也是一種方法,下面咱們的這種方式,也是先把每一個規則加入到List中去,只是執行的時候,有些不一樣:編寫責任鏈FilterChain,包含List屬性和相關方法,將規則(Filter實現類)加入到List中,以後取出一個規則執行,執行規則的業務邏輯方法以後,再回調FilterChain的doFilter,達到循環的目的。設計模式
二、編寫請求對象。這裏咱們假如處理告警信息ide
package com.yefengyu.entity; import java.util.Date; public class Alarm { //告警id private Integer id; //告警事件總數:這條告警是有幾條事件合併而成的 private Integer eventNumber; //告警名稱 private String alarmName; //告警發生位置 private String alarmAddress; //是否確認告警 0:確認 1:未確認 private Integer alarmAck; //告警等級 1:可疑 2:高危 3:嚴重 4:緊急 private Integer alarmLevel; //告警類型 1:停電 2:硬件 3:軟件 private Integer alarmType; //告警發送時間 private Date date; private String desc; public Alarm() { } public Alarm(Integer id, Integer eventNumber, String alarmName, String alarmAddress, Integer alarmAck, Integer alarmLevel, Integer alarmType, Date date, String desc) { this.id = id; this.eventNumber = eventNumber; this.alarmName = alarmName; this.alarmAddress = alarmAddress; this.alarmAck = alarmAck; this.alarmLevel = alarmLevel; this.alarmType = alarmType; this.date = date; this.desc = desc; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getEventNumber() { return eventNumber; } public void setEventNumber(Integer eventNumber) { this.eventNumber = eventNumber; } public String getAlarmName() { return alarmName; } public void setAlarmName(String alarmName) { this.alarmName = alarmName; } public String getAlarmAddress() { return alarmAddress; } public void setAlarmAddress(String alarmAddress) { this.alarmAddress = alarmAddress; } public Integer getAlarmAck() { return alarmAck; } public void setAlarmAck(Integer alarmAck) { this.alarmAck = alarmAck; } public Integer getAlarmLevel() { return alarmLevel; } public void setAlarmLevel(Integer alarmLevel) { this.alarmLevel = alarmLevel; } public Integer getAlarmType() { return alarmType; } public void setAlarmType(Integer alarmType) { this.alarmType = alarmType; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } @Override public String toString() { return "Alarm{" + "id=" + id + ", eventNumber=" + eventNumber + ", alarmName='" + alarmName + '\'' + ", alarmAddress='" + alarmAddress + '\'' + ", alarmAck=" + alarmAck + ", alarmLevel=" + alarmLevel + ", alarmType=" + alarmType + ", date=" + date + ", desc='" + desc + '\'' + '}'; } }
三、過濾器接口工具
package com.yefengyu.filter; import com.yefengyu.entity.Alarm; public interface Filter { void execute(Alarm alarm, FilterChain chain); }
四、責任鏈this
package com.yefengyu.filter; import com.yefengyu.entity.Alarm; import java.util.ArrayList; import java.util.List; public class FilterChain { //規則過濾器列表,實現Filter接口的過濾器將真正執行對事件的處理 private List<Filter> filters = new ArrayList<>(); //過濾器列表的索引 private int index = 0; //向責任鏈中加入過濾器(單個) public FilterChain addFilter(Filter filter) { this.filters.add(filter); return this; } //向責任鏈中加入過濾器(多個) public FilterChain addFilters(List<Filter> filters) { this.filters.addAll(filters); return this; } //處理事件(alarm)從FilterChain中獲取過濾器,進行處理,處理完成以後過濾器會再調用該方法, //繼續執行下一個filter.這就須要在每一個Filter接口的實現類中最後一句須要回調FilterChain的doFilter方法。 public void doFilter(Alarm alarm, FilterChain chain) { if (index == filters.size()) { return; } Filter filter = filters.get(index); index++; filter.execute(alarm, chain); } }
五、新增兩個規則,處理告警的邏輯單元spa
Rule1:設計
package com.yefengyu.rule; import com.yefengyu.entity.Alarm; import com.yefengyu.filter.Filter; import com.yefengyu.filter.FilterChain; public class Rule1 implements Filter { @Override public void execute(Alarm alarm, FilterChain chain) { //規則內容:若是是政府發生告警。告警等級設爲最高 if (alarm.getAlarmAddress().contains("政府")) { alarm.setAlarmLevel(4); System.out.println("執行規則1"); } //注意回調FilterChain的doFilter方法,讓FilterChain繼續執行下一個Filter chain.doFilter(alarm, chain); } }
Rule2code
package com.yefengyu.rule; import com.yefengyu.entity.Alarm; import com.yefengyu.filter.Filter; import com.yefengyu.filter.FilterChain; public class Rule2 implements Filter { @Override public void execute(Alarm alarm, FilterChain chain) { //規則內容:告警名稱爲:光功率衰耗,描述信息爲:割接 則該告警變爲確認狀態 if (alarm.getAlarmName().contains("光功率衰耗") && alarm.getDesc().contains("割接")) { alarm.setAlarmAck(0); System.out.println("執行規則2"); } //注意回調FilterChain的doFilter方法,讓FilterChain繼續執行下一個Filter chain.doFilter(alarm, chain); } }
六、客戶端
package com.yefengyu; import com.yefengyu.entity.Alarm; import com.yefengyu.filter.FilterChain; import com.yefengyu.rule.Rule1; import com.yefengyu.rule.Rule2; import java.util.Date; public class Client { public static void main(String[] args) { //構造告警事件 Alarm alarm = new Alarm(1, 10, "光功率衰耗", "省政府23號樓", 1, 1, 1, new Date(), "割接"); //將規則加入責任鏈 FilterChain filterChain = new FilterChain(); filterChain.addFilter(new Rule1()).addFilter(new Rule2()); //執行責任鏈 filterChain.doFilter(alarm, filterChain); //輸出結果 System.out.println(alarm); } }
結果:
執行規則1
執行規則2
Alarm{id=1, eventNumber=10, alarmName='光功率衰耗', alarmAddress='省政府23號樓', alarmAck=0, alarmLevel=4, alarmType=1, date=Sat Jun 29 17:47:27 CST 2019, desc='割接'}
上面的代碼已是使用責任鏈來完成了功能,若是咱們想新添加一個規則,只需實現Filter接口,而且重寫execute方法,將新添加的規則過濾器加入責任鏈中便可。也就是完成如下兩步便可:
實現Filter接口並重寫execute方法:Rule3
客戶端添加該規則過濾器到責任鏈便可。filterChain.addFilter(new Rule1()).addFilter(new Rule2()).addFilter(new Rule3());
上面代碼完成了咱們開始的預想,若是新添加規則,不須要在原有規則的基礎修改,而是新添加一個規則,而且加入到責任鏈中,這樣就能夠執行對應的規則,可是這樣也有個問題,咱們並不想顯示的將規則加入到責任鏈,若是繼承接口便可自動加入到責任鏈,這樣的話能夠把核心邏輯與規則分開,其實經過註解便可完成這項需求。
一、添加依賴
<dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.10</version> </dependency>
二、自定義註解
package com.yefengyu.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface EnableFilter { String value() default ""; }
三、在每一個規則(Rule1 Rule2)上面都加上EnableFilter註解。
四、增長一個掃描註解修飾的類,並將這些類實例對象返回
package com.yefengyu.factory; import com.yefengyu.annotation.EnableFilter; import com.yefengyu.filter.Filter; import org.reflections.Reflections; import java.util.ArrayList; import java.util.List; import java.util.Set; public class FilterFactory { public static List<Filter> getFilters(String packages) { List<Filter> filterList = new ArrayList<>(); //經過註解掃描指定的包 Reflections reflections = new Reflections(packages); //若是該包下面有被EnableFilter註解修飾的類,那麼將該類的實例加入到列表中,並最終返回 Set<Class<?>> filters = reflections.getTypesAnnotatedWith(EnableFilter.class); for (Class filter : filters) { try { filterList.add((Filter)filter.newInstance()); } catch (Exception e) { e.printStackTrace(); } } return filterList; } }
五、修改客戶端
package com.yefengyu; import com.yefengyu.entity.Alarm; import com.yefengyu.factory.FilterFactory; import com.yefengyu.filter.FilterChain; import com.yefengyu.rule.Rule1; import com.yefengyu.rule.Rule2; import java.util.Date; public class Client { public static void main(String[] args) { //構造告警事件 Alarm alarm = new Alarm(1, 10, "光功率衰耗", "省政府23號樓", 1, 1, 1, new Date(), "割接"); //將規則加入責任鏈中,經過註解掃描指定的包,此處無需指定執行哪一個規則(FIlter的實現類) FilterChain filterChain = new FilterChain(); filterChain.addFilters(FilterFactory.getFilters("com.yefengyu.rule")); //執行責任鏈 filterChain.doFilter(alarm, filterChain); //輸出結果 System.out.println(alarm); } }
其中
//將規則加入責任鏈中,經過註解掃描指定的包,此處無需指定執行哪一個規則(FIlter的實現類) FilterChain filterChain = new FilterChain(); filterChain.addFilters(FilterFactory.getFilters("com.yefengyu.rule"));
把下面的代碼替換了:
//將規則過濾器加入責任鏈中 FilterChain filterChain = new FilterChain(); filterChain.addFilter(new Rule1()).addFilter(new Rule2());
這個時候,若是想增長一個規則,那麼就只須要在 com.yengyu.rule的包下面新增一個規則,而後加上註解@EnableFilter便可將該規則加入到責任鏈中。
六、上面客戶端的責任鏈並無手動添加規則過濾器的實現類,經過FilterFactory自動掃描指定的包下面的被EnableFilter註解修飾的類,這樣達到了動態添加規則,又不影響主體代碼的效果。
七、若是想排除某些規則該怎麼辦?須要使用配置文件,添加一個工具類,讀取配置文件的內容,而後去除對應的過濾器,實現略。
代碼詳見:設計模式