你没见过的责任链设计模式!【设计模式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 }
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上下文。
哎,感觉数学还是要学好,虽然你可以不懂,但是底层都有数学基础做支撑的。另外设计模式看类图已经没那么实用了,因为方法成为第一等公民后,函数编程已经来临,大部分设计模式可能都会有新的表述。