关闭

Template method vs Callback模式

标签: callbackinterfaceobjectservicejaxbxml
1217人阅读 评论(0) 收藏 举报
分类:

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
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:280860次
    • 积分:3897
    • 等级:
    • 排名:第8139名
    • 原创:108篇
    • 转载:25篇
    • 译文:0篇
    • 评论:69条
    最新评论
    文章收藏