2007年8月10日星期五

[Tips]javascript设计模式漫谈之使用委托

文: jno <jno[at]ph4nt0m.org>
http://www.ph4nt0m.org

Text Mode

.net语言中有委托这个概念,委托的本质可以说是面向对象语言中安全的函数指针,javascript语言标准中虽然没有委托概念,但是却可以使用委托。因为在javascript是一个基于对象的语言,函数也是对象,所以自然就可以作为参数传递,于是也就可以使用委托机制。

这里举一个委托在异步调用中例子,来说明委托的用法,例子是对XmlHttp的简单封装:

<script type="text/javascript">
<!--
/*
* XmlHttp 类
*/
var XmlHttp = function() {
    
var o = null;
    
    
var readyStateChange = function(processResponseProc) {
        
if (o.readyState == 4) {
            
if (o.status == 200) {
                processResponseProc(o.responseText);
            } 
else {
                processResponseProc(
null);
            }
        }
    };

    
return {
        
/*
         * init 方法
         *
        * 返回值
         * 如果初始化成功则返回 true,否则返回false
        *
         * 说明
         * 初始化XmlHttp对象,
        
*/
        init : 
function() {
            
if (o == null) {
                
if (window.XMLHttpRequest) {
                    
try {
                        o 
= new XMLHttpRequest();
                    } 
catch (ex) {
                        
return false;
                    }
                } 
else if (window.ActiveXObject) {
                    
try {
                        o 
= new ActiveXObject("Msxml4.XMLHttp");
                    } 
catch (ex) {
                        
try {
                            o 
= new ActiveXObject("Msxml2.XMLHttp");
                        } 
catch (ex) {
                            
try {
                                o 
= new ActiveXObject("Microsoft.XMLHttp");
                            } 
catch (ex) {
                                
return false;
                            }
                        }
                    }
                }
            }

            
return true;
        },

        
/*
         * get 方法
         *
        * 参数
         * url  - 要请求的url
         * processResponse  - 处理返回结果委托
         *
        * 返回值
        * 如果请求发起成功则返回true,否则返回false
         *
         * 说明
        * 发起http请求
        
*/
        get : 
function(url, processResponse) {
            
try {
                o.open(
"GET", url, true);
                o.onreadystatechange 
= function() { readyStateChange(processResponse); };
                o.send();
                
return true;
            } 
catch (ex) {
                
return false;
            }
        },

        
/*
         * post 方法
         *
        * 参数
         * url  - 要请求的url
         * data -  发送的数据,注意这里值必须是urlencode过的
        * processResponse - 处理返回结果委托
        *
         * 返回值
         * 如果请求发起成功则返回true,否则返回false
         *
        * 说明
         * 发起post请求
        
*/
        post : 
function(url, data, processResponse) {
            processResponseProc 
= processResponse;
            
try {
                o.open(
"POST", url, true);
                o.onreadystatechange 
= function() { readyStateChange(processResponse); };
                o.setRequestHeader(
"Content-Length", data.length);
                o.setRequestHeader(
"Content-Type""application/x-www-form-urlencoded");
                o.send(data);
                
return true;
            } 
catch (ex) {
                
return false;
            }
        }
    };
};

var xmlhttp = new XmlHttp();
if (xmlhttp.init()) {
    xmlhttp.get(
"http://pstgroup.blogspot.com/"function(response) {
        
if (response != null) {
            document.write(response);
        }
    });
}
//-->
</script>


这里get和post方法都是异步调用,都需要传入有一个processResponse参数,这个参数是http返回内容处理函数,也就是我们所说的委托概念,对象内部会在http请求返回后回调该函数来处理返回的内容,委托这个词很形象,XmlHttp类并不知道调用者要对返回结果干些什么,于是就委托给这个传递近来的函数来做。

委托可以很好的降低不同功能之间的耦合,这个例子使用委托就符合Open-Close原则,如果有新的处理返回结果的需求可以很容易扩展,不用改动现有代码,比如我们的新需求是alert返回结果:

var xmlhttp = new XmlHttp();
if (xmlhttp.init()) {
    xmlhttp.get(
"http://pstgroup.blogspot.com/"function(response) {
        
if (response != null) {
            alert(response);
        }
    });
}


委托之所以适合异步调用,是因为异步调用符合委托的使用场景:调用者A调用被调用者B,由于调用过程是异步的,不会立刻返回结果,所以B在拿到返回结果时,有需要依赖于A来处理结果。当在类设计时发现类之间双向互相依赖的情况时,可以考虑使用委托。

没有评论: