博客园  :: 首页  :: 管理

Decorator模式[收藏]

Posted on 2006-02-20 10:42  Paker Liu  阅读(346)  评论(0编辑  收藏  举报

不知道在这儿说“Decorator模式是否合适,JS不是面向对象的语言,又是弱语言类型,设计模式是面向对象语言的。两者似乎相差很远。

不过正是因为JS是弱语言类型,造成这种“Decorator模式。其实作为模式的一种,Decorator或是其它模式没有必要一定要有固定的UML图,或是一定要是面向对象的,它只是一种思想。我理解的Decorator是:

给一个对象添加一些额外的职责

 

如果我们把JS中的一些函数也看成是对象的话,我们再来看看在JS中如何对它们进行装饰

一、利用JS“重载方法

突然发现JS有一种很好的机制(实际上是一位JS高人指点我的,嘿嘿),它可以重载一些函数,利用这种功能我们可以进行动态重载。参考以下代码:

window.alert=a;

function a()

{

      window.location
='http://localhost/a.aspx';

}


然后我们在这个页面上来一个alert(‘abc’)会有什么反应?页面不会弹出提示框,而是转向上边这个URL。也就是说运行alert时是执行函数a,而不是系统默认的函数了。

 

二、使用JS“重载为老函数添加新的职责

      我们可以在老函数的基础上动态地添加新的功能,同时又保持原来的函数签名。我认为这已经做成了Decorator,参考以下代码:

运行一下会得到什么在提示消息出来后页面跳转到http://localhost/quickstart/

<BODY>

<SCRIPT LANGUAGE="JavaScript">

<!--

var temp=window.alert;

window.alert
=a;

function a(alertValue)

{

      temp(alertValue);

      window.location
='http://localhost/quickstart/';

}


 

      alert(' Microsoft 快速入门教程');

//-->

</SCRIPT>

</BODY>


过程:

通过声明一个temp变量来保存系统原来的alert函数;

把系统的函数赋上新的方法(函数指针?^_^),新的方法意味着新的职责。

在新的方法中再调用temp中保存的原来的系统函数。

 

三、实际应用

某个项目有不少页面用到了window.open方法,且实现方法各种各样,有直接写在JS中的,有在codebehindresponse的,也有利用.NET注册脚本块的,而且各个参数也不同。突然在项目中有需要变更,要求所有的popup全部居中,如果此时对window.open方法一个一个找出来再修改的话,估计会让人疯掉。

这种情况下我们可以在所有页面的基类(commonpage)重写window.open,为它添加居中的职责并保持原来的函数签名,即那三个参数不变,从而实现对项目中所有的open都居中。

参考代码如下:

  'by dingsea 11-21
    'for adjusting the pop window to center.
    Private Function RegisterScriptToAdjustPopWindow() As String
        
Dim sPop As StringBuilder = New StringBuilder("<SCRIPT LANGUAGE=" + Chr(34+ "JavaScript" + Chr(34+ ">" + Chr(13))
        sPop.Append(
"var o=window.open;" + Chr(13))
        sPop.Append(
"window.open=launchCenter;" + Chr(13))
        sPop.Append(
"function launchCenter(url,name,features)" + Chr(13))
        sPop.Append(
"{" + Chr(13))
        sPop.Append(
"var width=features.substring((features.indexOf('width=')+6));" + Chr(13))
        sPop.Append(
"width=width.substring(0,width.indexOf(','));" + Chr(13))
        sPop.Append(
"var height=features.substring((features.indexOf('height=')+7));" + Chr(13))
        sPop.Append(
"height=height.substring(0,height.indexOf(','));" + Chr(13))
        sPop.Append(
"features+=',innerHeight='+height;" + Chr(13))
        sPop.Append(
"features+=',innerWidth='+width;" + Chr(13))
        sPop.Append(
"if (window.screen) {" + Chr(13))
        sPop.Append("     features += ',top='+(screen.availHeight- height)*0.5+',left='+(screen.availWidth-width)*0.5;" + Chr(13))
        sPop.Append(
"}" + Chr(13))
        sPop.Append(
"o(url,name,features);" + Chr(13))
        sPop.Append(
"}" + Chr(13))
        sPop.Append(
"</script>")

        Return sPop.ToString

    End Function

 在页面加载时使用以上代码:

Private Sub Page_PreRender(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.PreRender
Response.Write(RegisterScriptToAdjustPopWindow()) 
'by dingsea
    End Sub

为什么不使用.NET自带的注册脚本块?我是想让这个重写代码在页面的最上方,如果用注册脚本块,会出现在BODY之内或是别的什么地方,而如果我直接在page render的时候就response,它会在HTML和所有JS之前被发送到客户端,从而保证页面任何window.open都可以居中。