Template method vs Callback模式

Template method vs Callback
以前曾在《也谈谈Spring中的Template和Callback模式》中谈过这两个模式的实用场合,那时候没有给出代码。
最近遇到一个问题,可以很好的说明这两种模式的使用场合。
需求:我暑假实习公司做的一个产品,我们Server组主要事情是:一、把页面的信息组装成DTO,使用JAXB技术
将定义的消息格式(xml schema)自动生成对应的Object,把DTO装配对应的Object,然后marsh成xml(JAXB是什么
东东可以参见以前我写的《尝试了一下JAXB2.0》),最后把消息发送给Client端。二、从Client端接收到xml,然
后unmarch成Object,然后把Object装配成DTO,最后传到页面,来设置页面。
我们可以看到整个过程非常的固定,无非是Send还是Get信息的问题。所以我们很容易想到使用摸版来解决,事实上我们最常用的Servlet也是这两种过程Post和Get,它同样使用了摸版方法。
举一个例子,一个Site里有许多Rules,我们要操作Rules(例如增删改之类的操作),不同的操作过程可能不同,
例如新增一个Rule,我们只需要做上面说的第一件事情,而如果要修改Rule则两件事情都要做。我们现在就用模版
方法来实现:

public   interface  CAIBaseService{
       
public  String send( long  agentId, String fullPath,  int  typeId,Object dto);
       
public  Object get( long  agentId,String fullPath, int  typeId);
}

public   class  CABaseServiceTemplate  implements  CAIBaseService{
    
public  String send( long  agentId, String fullPath,  int  typeId,Object dto){
            RequestBean reqBean 
=  doPrepareToSend(agentId,fullPath,typeId,dto); // 把dto装配成Object[requestBean]
            Response response  =  doProcessRequest(reqBean); // 真正把xml发送给Client端,这个过都是一样的,可以抽象出NetworkService来处理
             return  doProcessSendResponse(response); // 处理返回的response,例如根据response判断是否发送成功,以及失败的原因
    }
    
public  Object get( long  agentId,String fullPath, int  typeId){
           RequestBean reqBean 
=  doPrepareToGet(agentId,fullPath,typeId); // 装配成Object[requestBean]的根,这样Client就知道消息是什么类型的,从而设置信息返回
           Response response  =  doProcessRequest(reqBean); // 真正把xml发送给Client端,这个过都是一样的,可以抽象出NetworkService来处理
            return  doProcessGetResponse(response); // 处理返回的response,例如把response装配成Dto,判断是请求否发送成功,以及失败的原因
    }
    
protected  RequestBean doPrepareToSend( long  agentId, String fullPath,  int  typeId,Object dto);
    
protected  String doProcessSendResponse(Response response);
    
protected  RequestBean doPrepareToGet(agentId,fullPath,typeId);
    
protected  Object doProcessGetResponse(Response response);
}
//     对于Rule
class  AddRuleServiceImpl  extends  CABaseServiceCallbackTemplate {
     
protected  RequestBean doPrepareToSend( long  agentId, String fullPath,  int  typeId,Object dto){
      
// ...
     }
     
protected  String doProcessSendResponse(Response response){
      
// ...
     }
           
protected  RequestBean doPrepareToGet(agentId,fullPath,typeId){
     
// 因为我们不需要这个操作所以,空实现
     }
     
protected  Object doProcessGetResponse(Response response){
     
// 因为我们不需要这个操作所以,空实现
     }
    }

class  EditRuleServiceImpl  extends  CABaseServiceCallbackTemplate {
    
protected  RequestBean doPrepareToSend( long  agentId, String fullPath,  int  typeId,Object dto){
      
// ...
    }
    
protected  String doProcessSendResponse(Response response){
      
// ...
    }
    
protected  RequestBean doPrepareToGet(agentId,fullPath,typeId){
     
// ...
    }
    
protected  Object doProcessGetResponse(Response response){
     
// ...
    }
}

 

我们发现对于一个Rule就需要三到四个Service,Service的粒度显得有点太细,因为我们我们继承了CABaseServiceCallbackTemplate,我们Service的粒度已经由CABaseServiceCallbackTemplate

控制了,
这就是继承的不灵活性,同时我们看到我们在AddRuleServiceImpl中还需要空实现两个方法,当然我们
可以继续对CABaseServiceCallbackTemplate封装出一个新的Template,将他们都默认空实现了CABaseServiceCallbackTemplate未实现的方法,这样我们在AddRuleServiceImpl就不需要空实现了,

另外,
doPrepareToSend,doProcessSendResponse到底是干什么的(由于Send方法在CABaseServiceCallbackTemplate
定义的),别人头一次看说不定发甍。综上,在这里使用Template method模式,产生了过于琐碎的类,并且
显得不够直观。
下面我们考虑使用Callback模式来实现,来避免上面的问题:

 

public   interface  CAIBaseService{
       
public  String send( long  agentId, String fullPath,  int  typeId,Object dto);
       
public  Object get( long  agentId,String fullPath, int  typeId);
}

public   interface  CAProcessSendResponseCallback{ 
        RequestBean doPrepareToSend(
long  agentId,String fullPath, int  typeId,Object dto);
}

pubilc 
interface  CAProcessSendResponseCallback{
    String  doProcessSendResponse(Response response);
}

public   interface  CAPrepareToGetCallback{
    RequestBean doPrepareToGet(
long  agentId,String fullPath, int  typeId);
}

public   interface  CAProcessGetResponseCallback{
    
public  Object doProcessGetResponse(Response response);
}
    
public   class  CABaseServiceCallbackTemplate  implements  CAIBaseCallbackService{
      
public  String send( long  agentId, String fullPath,  int  typeId,Object dto,
               CAPrepareToSendCallback prepareToSendCallback,CAProcessSendResponseCallback processSendResponseCallback){
      
          RequestBean reqBean 
=  prepareToSendCallback.doPrepareToSend(agentId,fullPath,typeId,dto); //  把dto装配成Object[requestBean]
          Response response  =  doProcessRequest(reqBean); //  真正把xml发送给Client端,这个过都是一样的,可以抽象出NetworkService来处理
           return  processSendResponseCallback.doProcessSendResponse(response); //  处理返回的response,例如根据response判断是否发送成功,以及失败的原因
      }
     
public  Object get( long  agentId,String fullPath, int  typeId,CAPrepareToGetCallback prepareToGetCallback,
             CAProcessGetResponseCallback processGetResponseCallback){
                RequestBean reqBean 
=  processSendResponseCallback.doPrepareToGet(agentId,fullPath,typeId); //  装配成Object[requestBean]的根,这样Client就知道消息是什么类型的,从而设置信息返回
             Response response  =  doProcessRequest(reqBean); //  真正把xml发送给Client端,这个过都是一样的,可以抽象出NetworkService来处理
              return  processSendResponseCallback.doProcessGetResponse(response); //  处理返回的response,例如把response装配成Dto,判断是请求否发送成功,以及失败的原因
     }
    }

    
public   class  CASiteRuleManageServiceImpl {  
         
private  CABaseServiceCallbackTemplate caBaseServiceCallBackTemplate;
      
         
public  CASiteNewRuleDto getEditRuleInfo( long  agentId, String fullPath,  int  typeId) { 
               
return  (CASiteNewRuleDto)caBaseServiceCallBackTemplate.get(agentId, fullPath, typeId, new  CAPrepareToGetCallback (){
                     
public  RequestBean doPrepareToGet( long  agentId, String fullPath,  int  typeId){
                              
//  ...
                     }
                }
               ,
new  CAProcessGetResponseCallback (){
                     
protected  Object doProcessGetResponse(Response response){
                        
//  ...
                     }
               }
          );
     }

     
public  String sendEditRuleInfo( long  agentId, String fullPath,  int  typeId, CASiteNewRuleDto dto) {
      
return  caBaseServiceCallBackTemplate.send(agentId, fullPath, typeId, dto,  new  dto,CAPrepareToSendCallback() {
              
public  RequestBean doPrepareToSend( long  agentId, String fullPath,  int  typeId,Object object){
                  
//  ...
              }
             },
               
new  CAProcessSendResponseCallback(){
                     
public   String doProcessSendResponse(Response response){
                          
//  ...
                     }
               }
             ); 
     }

     
public  String sendAddRuleInfo( long  agentId, String fullPath,  int  typeId, CASiteNewRuleDto dto) {
           
return  caBaseServiceCallBackTemplate.send(agentId, fullPath, typeId, dto,  new  dto,CAPrepareToSendCallback() {
                   
public  RequestBean doPrepareToSend( long  agentId, String fullPath,  int  typeId,Object object){
                       
//  ...
                   }
                },
                
new  CAProcessSendResponseCallback(){
                   
public   String doProcessSendResponse(Response response){
                         
//  ...
                   }
                }
            ); 
//  ...other operation
    }
}

 

我们发现使用Callback模式,我们可以把对Rule的操作组织成一个Service,避免产生大量的Service,同时我们可以随意使用Get和Send操作,而不象Template method受缚于上层定义步骤,以及要实现的方法,同时我们可以给Service的方法很好的命名(例如sendAddRuleInfo),从而使得程序更加清晰。唯一的不完美的地方是使用了内部匿名类,给程序的阅读带来点困难,当然可以用内部类来代替匿名的,但大家似乎更习惯使用匿名内部类,如果Java象Ruby那样能够支持block就好了(Java 7据说要支持closure,但由于Java历史的包袱太重,实现是何等的丑陋,非泛型所能比--Java为了向前兼容,泛型的实现实在难以和c++和c#相比)。
从上面分析可以看到如果不是真正的"is-a"关系的话,组合优于继承绝对是至理名言,继承就会对子类有所限制,而组合则没有什么限制,显得更加灵活。一般情况下如果一个操作的子步骤比较少(三个以下),特别是如果你不想生成太细粒度的对象时,用Callback来代替template method能够获得更好的灵活性,子步骤多则使用template method. 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值