你沒見過的責任鏈設計模式!【設計模式2】


New責任鏈&裝飾者】

感慨一下,本以為上下篇能一起發呢,結果要隔一定時間,傳統的責任鏈與裝飾者模式:https://www.cnblogs.com/SharePointApp/p/10340578.html

基本代碼

現在要做的就是責任鏈如果使用外置模式,能不能像裝飾者一樣幾個處理類聯合處理?答案是可以的,不過就用到java8的特性,具體代碼如下

1 //簡化的上下文
2 public class Context {
3     String handlerResult;
4 }
5 
6 //處理接口
7 public interface IHandler {
8     void handler(Context context);
9 }
接口與上下文
1 //默認Null接口實現
2 public class NullHandler implements IHandler {
3     @Override
4     public void handler(Context context) {
5 
6     }
7 }
默認Null接口實現
1 //接口的鏈式結構
2 public interface IMiddleware {
3     IHandler handle(IHandler handler);
4 }
接口的鏈式結構
 1 //構建鏈式結構
 2 public class MiddlewareCenter {
 3     private ArrayList<IMiddleware> middlewares = new ArrayList<>();
 4 
 5     public void userMiddleware(IMiddleware middleware) {
 6         middlewares.add(middleware);
 7     }
 8 
 9     public void use(IHandler handler) {
10         this.userMiddleware(currenthandler -> {
11             return context -> {
12                 handler.handler(context);
13                 currenthandler.handler(context);
14             };
15         });
16     }
17 
18     public void run(IHandler handler) {
19         this.userMiddleware(currenthandler -> {
20             return context -> {
21                 handler.handler(context);
22             };
23         });
24     }
25 
26     public IHandler build() {
27         IHandler handler = new NullHandler();
28         Collections.reverse(middlewares);
29         for (IMiddleware middlerware : middlewares) {
30             handler=middlerware.handle(handler);
31         }
32         return handler;
33     }
34 }
【重點】-鏈式結構的構建

下面貼的是測試代碼,略顯冗長

 1 public class Executor {
 2     public static void main(String[] args){
 3         Context context=new Context();
 4         context.handlerResult="";
 5         MiddlewareCenter center=new MiddlewareCenter();
 6         center.use(Executor::addString);
 7         center.use(Executor::addString2);
 8         center.use(Executor::modifyString);
 9         center.build().handler(context);
10         System.out.println(context.handlerResult);
11 
12         Context context1=new Context();
13         context1.handlerResult="";
14         MiddlewareCenter center1=new MiddlewareCenter();
15         center1.use(Executor::addString);
16         center1.use(Executor::modifyString);
17         center1.use(Executor::addString2);
18         center1.build().handler(context1);
19         System.out.println(context1.handlerResult);
20 
21         Context context2=new Context();
22         context2.handlerResult="";
23         MiddlewareCenter center2=new MiddlewareCenter();
24         center2.use(Executor::addString);
25         center2.run(Executor::modifyString);
26         center2.use(Executor::addString2);
27         center2.build().handler(context2);
28         System.out.println(context2.handlerResult);
29     }
30 
31     private static void addString(Context context){
32         context.handlerResult=context.handlerResult+"addString;";
33     }
34 
35     private static void addString2(Context context){
36         context.handlerResult=context.handlerResult+"addString2;";
37     }
38 
39     private static void modifyString(Context context){
40         context.handlerResult=context.handlerResult.replace("addString","addString->modifyString");
41     }
42 }

執行結果如下:

代碼解析

先說說好處:

  • 在Java8中,lamada表達式以及方法已經是一類公民,可以減少很多不必要的子類擴展。所以在組裝的時候IHandler對象的時候,沒有必要構建很多IHandler對象(傳統的責任鏈、裝飾者模式也可以,利用lamada表達式)。
  • 在MiddleWareCenter中,可以直接將IHandler對象組裝在一起,和責任鏈模式外置類似,但是也可以用IMiddleWare對象,使用內置的方式將IHandler組裝起來。使代碼有很大的靈活性。
  • 總的說來就是使用的時候既簡單,又強大,還有代碼看起來更時髦。

缺點呢?

  • 相比於責任鏈內置、責任鏈外置、裝飾者模式,沒有什么缺點,不過代碼略有一點難以理解

 核心代碼解析

1 public IHandler build() {
2         IHandler handler = new NullHandler();
3         Collections.reverse(middlewares);
4         for (IMiddleware middlerware : middlewares) {
5             handler=middlerware.handle(handler);
6         }
7         return handler;
8     }

這句代碼可能是最難以理解的,我們知道middlewares的類型是IArrayList<IMiddleWare>,所以這段代碼就是翻轉集合,之后遍歷組裝。怎么遍歷組裝的,就得用數學知識進行簡單的講解了

我們知道IMiddleWare的handler方法是一個IHandler到IHandler的函數(略思考,這沒難度),我們將IHandler用自變量X代替,所以IArrayList<IMiddleWare>就是函數F(X)的集合(假設里面的函數是A(X)、B(X)、C(X))。

翻轉集合之后,遍歷組裝的結果是C(B(A(X))),這是一個復合函數,但是本質上還是一個F(X),即IHandler到IHandler的函數。而IHandler到IHandler的函數,我們給X的值就是NullHandler,所以帶入復合函數C(B(A(X)))后得到C(B(A(NullHandler)))。這個結果是一個IHandler對象,他可以用於處理Context上下文。

哎,感覺數學還是要學好,雖然你可以不懂,但是底層都有數學基礎做支撐的。另外設計模式看類圖已經沒那么實用了,因為方法成為第一等公民后,函數編程已經來臨,大部分設計模式可能都會有新的表述


免責聲明!

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



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