`
xzer
  • 浏览: 16624 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

友好的开发框架-Asta4D(6):副作用与Request Handler

阅读更多

1. Http请求的责任人-Request Handler

 

Asta4D虽然秉承View First的原则,但并不意味着在Asta4D中没有类似于MVC架构中Controller的存在,Request Handler可以看作是MVC架构中Controller的替代者。我们认为,对于每一个特定的Http请求,系统中理应存在一个角色对该请求承担责任,这个责任承担者,在Asta4D中,被称之为Request Handler。

 

基于View First的原则,在大多数时候,Request Handler是被省略的,或者可以理解为框架提供了一个缺省的Request Handler,它负责将请求导向对应的模板(实际实现上比这个要复杂一些,但概念上可以如此理解),那么,我们什么时候需要一个特定的Request Handler呢?

 

在回答这个问题之前,我们先讨论一下关于“副作用”的概念。

 

2. 系统操作的“副作用”

 

一个供用户使用的系统,必然提供诸多操作,简单的描述,可以说一个系统通常会提供查询操作,更新操作,等等等等。在这诸多操作中,我们需要意识到一个问题,就是不同的操作会给系统带来不同的影响,在Asta4D的思维方式中,我们认为,系统操作可以分为两个类型:具有“副作用”和不具有“副作用”。

 

一个具有“副作用”的操作,会显著的改变系统的状态,比如对于同一个url请求,在执行完一个login请求之后(如果成功的话),因为客户端权限的改变,很可能就会得到一个不同于login之前的页面应答。另一个更为显著的例子是数据库更新操作,一旦用户请求了一个更新操作,那么所有相关的客户端都会看到一个不同于更新前的数据—这就是所谓的“副作用”。

 

那么什么是没有“副作用”的操作呢?比如查询操作,无论你请求多少次同样的查询,你的请求都不会对系统状态造成改变,因此,一个查询操作是没有副作用的操作。有人会提到,如果我们对查询次数进行计数呢?我们仍然可以把计数操作和真正的查询操作分开成两个独立的操作,一个具有副作用,而另一个没有。

 

3. 隔离“副作用”与多线程渲染

 

我们认为,具有“副作用”的操作是需要慎重对待的,而Asta4D提供的解决方案,就是通过Request Handler封装并隔离所有具有副作用的操作。通过将所有的副作用隔离在Request Handler这一层,从而使得渲染层的逻辑变得更为简洁并富有弹性:既然所有的副作用都被隔离在Request Handler中,因此,我们可以非常轻松的实现多线程渲染--这是Asta4D极富特色的功能之一。

 

在传统的MVC架构中,Controller的职责是混乱的,既要承担改变系统状态的职责(数据更新),又要负责为View层准备数据,而为了解决延迟加载而经常不得不将数据库会话(事务)扩散到View层,使得事务管理也更加复杂。另一方面,特别是对于某些被分割为相对独立的区块的页面来说,虽然理论上可以通过多线程来为页面的不同区块同时准备数据从而提高响应速度,但这加大了代码的复杂度以及开发成本,在开发实践当中并不现实,因此,即使页面被分割为相对独立的区块,通常程序员也不得不顺序访问数据源为每个区块准备数据。

 

而对于Asta4D来说,通过在架构上分离副作用,从而使得系统的逻辑分层更为清晰。数据库会话(事务)扩散的问题不复存在,Request Handler的职责相当明确,只负责改变系统状态(数据更新)而不再负责为View层准备数据,而在渲染层逻辑中,数据的获取与渲染是同时完成的:我们在snippet方法中访问数据源,查询数据,同时立即在snippet方法中将数据传递给Renderer完成渲染逻辑。同时,因为对特定数据的访问被隔离在特定的snippet方法中,不再有跨层的调用和耦合,系统的复杂度也因此大为下降。

 

另一方面,我们只需要将不同的snippet方法的调用多线程化,就能够简单的实现多线程渲染,这一切对程序员都是透明的,而且因为所有的副作用已经被隔离在Request Handler层,即使我们对页面进行多线程渲染,也不需要考虑线程同步与互斥的问题--所有的view层的操作都是没有副作用的。

 

来看看我们如何简单的实现页面的多线程渲染:

 

<div afd:render="ParallelTest$TestRender:snippetInDiv" afd:parallel>
    <div id="test">xx</div>
</div>

<afd:snippet render="ParallelTest$TestRender:snippetReplaceDiv" parallel>
    <div id="test">xx</div>
</afd:snippet>

 

所有声明了parallel(afd:parallel)的snippet,都不会阻塞http request的主线程,http request的主线程会在完成所有的非parallel渲染后等待parallel渲染的结果返回,然后将合并的结果返回给客户端。

 

4. Request Handler与MVC的兼容性

 

有人大概会问一个问题:我们真的不需要MVC吗?真的不需要Controller吗?答案是No。我们承认,对于很多特定的用例来说,MVC的结构要比View First更为方便,特别是在处理Form提交的时候,很自然的就会要求MVC的架构。那么在Asta4D框架下,你只需要简单的将Request Handler当作Controller来实现就是了。

 

更进一步的,也许你无论如何都无法理解或者接受View First的理念,你认为Controller一定是必须的,但Asta4D分离的模板机制又让你动心的话,仍然是同样的回答:你只需要简单的将Request Handler当作Controller来实现就是了。

分享到:
评论
8 楼 Sugar 2016-06-13  
引用

当然,也可以说这个的确算不上漂亮,但至少不算难看,可以看作是对于request handler声明机制的一个妥协。其实我们也希望可以指定到方法,但既然要到Java8才支持method reference,那么在Java7的阶段,要指定到方法,唯一的办法是在声明handler的时候就用类似声明snippet方法的格式用字符串来声明了,这个味道让我们感觉不太舒服。我们希望能够尽可能的在代码中保持引用,当然,就snippet的声明来说,我们的确没有找到可以在html文件保持Java引用的办法


引用

让我们等待伟大的Java8吧,Java8会让Asta4D的很多代码变得更为简洁,因为我们本来就是从函数式编程跑过来的,很多api的设计都是在等Java8的函数式支持



现在有Java8 方式的声明到方法的吗?有没有相关实例?
7 楼 xzer 2014-01-15  
引用

看起来有点反人类的设计,哈哈。。建议提供Handler可以指定到方法级别,而不是类级别,或者两种方式都提供,用户有可以自由选择。


哈哈,我们有同事反而觉得这种写法很酷 你看,我们可以声明这样的rule:

rules.add("/addUser").handler(UserHandler.Add.class);


当然,也可以说这个的确算不上漂亮,但至少不算难看,可以看作是对于request handler声明机制的一个妥协。其实我们也希望可以指定到方法,但既然要到Java8才支持method reference,那么在Java7的阶段,要指定到方法,唯一的办法是在声明handler的时候就用类似声明snippet方法的格式用字符串来声明了,这个味道让我们感觉不太舒服。我们希望能够尽可能的在代码中保持引用,当然,就snippet的声明来说,我们的确没有找到可以在html文件保持Java引用的办法

让我们等待伟大的Java8吧,Java8会让Asta4D的很多代码变得更为简洁,因为我们本来就是从函数式编程跑过来的,很多api的设计都是在等Java8的函数式支持

额外多说一句,request handler的instance resolver和handler executor都是基于接口实现的,框架本身只是提供了一个缺省实现,这里的描述的策略,也只是框架的缺省实现的策略而已,用户完全可以参照框架的缺省实现自己实现自己的策略。
6 楼 semmy 2014-01-15  
根据你提供的方案,没感觉到灵活,比如我现在在把上面doupdate、doAdd、doDelete写成普通的方法,就无法转成handler,总感觉被死死的要求要用类级别的handler来处理。
引用

Asta4D的目的是给予程序员最大的灵活性,因此Asta4D并没有规定你应该如何组织你的代码,你可以自由的组织你的代码,只要Java允许,Asta4D就允许。
5 楼 semmy 2014-01-15  
public abstract UserHander{  
  
  protected void someCommonLogic(){  
     ...  
  }  
  
  public static class Update extends UserHander{  
      @RequestHandler  
      public void doUpdate(){  
      }  
  }  
  
  public static class Add extends UserHander{  
      @RequestHandler  
      public void doAdd(){  
      }  
  }  
  
  public static class Delete extends UserHander{  
      @RequestHandler  
      public void doDelete(){  
      }  
  }  
  
}  

看起来有点反人类的设计,哈哈。。建议提供Handler可以指定到方法级别,而不是类级别,或者两种方式都提供,用户有可以自由选择。
4 楼 xzer 2014-01-15  
这样子,个人觉得很容易让request handle类的数目膨胀起来,往往一个领域对象可能有几种的业务操作,那么想把这几个业务操作封装在同一个类。但目前做法,却是同一个领域对象的业务操作分散在多个类里。


Just change the way of thinking....

Asta4D的目的是给予程序员最大的灵活性,因此Asta4D并没有规定你应该如何组织你的代码,你可以自由的组织你的代码,只要Java允许,Asta4D就允许。你看,就你提到的case,我们同样存在,我们的解决办法很简单,假设有个用户相关的操作吧:

public abstract UserHander{

  protected void someCommonLogic(){
     ...
  }

  public static class Update extends UserHander{
      @RequestHandler
      public void doUpdate(){
      }
  }

  public static class Add extends UserHander{
      @RequestHandler
      public void doAdd(){
      }
  }

  public static class Delete extends UserHander{
      @RequestHandler
      public void doDelete(){
      }
  }

}
3 楼 semmy 2014-01-15  
xzer 写道
一个类只能有一个handle方法,不能有多个。

这样子,个人觉得很容易让request handle类的数目膨胀起来,往往一个领域对象可能有几种的业务操作,那么想把这几个业务操作封装在同一个类。但目前做法,却是同一个领域对象的业务操作分散在多个类里。
2 楼 xzer 2014-01-15  
一个类只能有一个handle方法,不能有多个。但是一个url可以配置多个request handler从而构成一个request handler chain。

关于request handler chain的说明,我还在修改中,下面摘一段简单说明一下,里面出现的一些新的名词,就暂时无视吧,看个意思就行了:

引用

如果一个request handler的handle方法直接返回一个Content Provider,框架会立即调用Content Provider的produce方法完成写入,同时,框架会判断该Content Provider是否可继续,如果不可继续,则后续的request handler不会被调用,当前请求被视为已经响应完毕,框架会立即返回。如果得到的content provider是可继续的,那么在produce方法完成后,框架会继续调用后续的request handler,并重复同样的过程。

以前的示例代码中我们可以看到,request handler通常并不会直接返回一个content provider,这个时候,框架会将request handler的结果按照顺序调用预设的result transformer将结果变换成特定的content provider。变换过程是反复迭代的,框架会将变换的结果一遍又一遍的通过result transformer进行变换,直到得到一个可用的content provider实例为止。

这里的result transformer是框架内建的一组变换规则,严格的讲,并不支持客户定制。但事实上,forward和redirect的结果匹配,就是利用result transformer的反复迭代来实现的,所以,forward和redirect的声明可以看作是对result transformer的有限度的定制化。

1 楼 semmy 2014-01-14  
一个类里是否只能写一个Request Handler方法?如果有多个,对应URL如何找到相应的Request Handler方法?

相关推荐

Global site tag (gtag.js) - Google Analytics