中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 软件工程 > UML > OO技术
面向对象方法在HTML编码中的应用
作者:未知 时间:2005-08-07 11:17 出处:系统分析之窗 责编:chinaitpower
              摘要:面向对象方法在HTML编码中的应用

面向对象方法在HTML编码中的应用


刘彦青编译

yesky

3/8/2001



  还记得曾经多少次地重写你曾写过数十次的HTML代码吗?即使你保存着以前的代码,为了在新的应用中重新使用这些代码你又花费了多少的时间?由于我经常会碰到这种情况,因此决心探索出一种新的方法。

  过去,许多的开发人员都建立了“可重复使用的”跨平台的DHTML代码库,但这些代码为大都存在着一些严重的缺点。首先,它们都不符合标准的面向对象方法,使得在使用、修改或扩充时非常不方便。其次,由于在每个脚本中都包含了适用于不同浏览器的代码,每当有新的浏览器推出时都会使代码变得更加臃肿、复杂。我在这篇文章中介绍的方法将在解决这些问题的基础上达到组件重用的目的。

  这篇文章将详细说明建立一个可重复使用的DHTML部件的可扩充结构的全过程,每个小节介绍这个过程的一个部分。

  实现可重用性的软件工程方法

  我们经常会保存以前编写过的代码,以便在其他的项目中重复使用。一旦有这种需求时,我们就会对原有的代码进行编辑,如果能记起当初是如何设计这些代码或它们的工作原理,那我们就太幸运了,然而往往事与愿违,我们已经忘记了有关这些代码的资料。尽管这些代码从技术上说是可重复使用的,但我们最多只能重复使用代码而不能重复使用组件,使用原来的代码可能带来新的代码质量问题。要编写高质量的代码,设计过程要比具体的编写过程更重要,如果你仓促地使用原来的代码,那就基本上跳过了设计阶段,你不能保证自己已经真正地理解了这些代码,不能删除其中你不需要的部分,甚至删除了应该保留的部分。

  由于我们的目标是建立一个真正的可重复使用和可扩展的结构,我们将这一问题看作是一个软件工程而不是一个网页脚本,到本篇文章结束时,我们将利用被称作面向对象的现代软件开发方法设计出一个脚本。

  在这里我们简单地介绍一下面向对象的设计方法,面向对象设计方法的主要特点是封装性、继承性和多态性。在建立可重复使用的组件时,最重要的是封装性,也就是说把组件作为一个“黑盒子”,组件的用户无需清楚组件内部的工作原理,用户也无需清楚一个对象的工作原理,只需知道如何使用它即可。关于JavaScript面向对象设计中的对象模式的详细说明请参阅网景公司的《客户端JavaScript指导》一书中的有关章节。

软件设计模式是解决软件设计过程中所出现问题的基础性的解决方案,它可以使我们无需做重复的工作━━即进行可重复使用性设计,比如设计可重复使用的代码。其中需要使用的重要设计模式包括GRASP模式:高内聚性和低耦合性,高内聚性指一个对象应该包含所有的需要直接使用的功能,低耦合性指把对象间需要交换的数据量降低到最少,这就要求我们在编码时要实现最大程度的抽象性和封装性,把各个组件间的相关性降低到最低。

  我们一定会这样想:既然DHTML代码也是软件,为什么不能让所有的开发人员使用统一的术语?在这篇文章中,我们将统一使用类、继承等Java语言的术语,其意义与Java语言中的意义类似。

  最后,我们将使用统一模式语言用图表划出我们设计的类结构,统一模式语言是业界广泛采用的一个面向对象设计标准。

  对浏览器进行抽象

  在开始设计之前,我们需要首先清楚各种浏览器之间的不兼容性是如何影响软件的可重用性的,因为我们不希望为每种浏览器编写相应的代码。因此我们需要剔除针对任何特定浏览器的代码,建立浏览器的抽象层,这是一种访问浏览器DOM的通用API,编程人员使用这些API而不是通过直接操作浏览器中的对象与浏览器进行交互。例如,在移动一个DIV时,我们不会使用mydiv.style.left = 100这样的语句,而只需使用SetDivLeft(mydiv,100)就能完成相应的任务。我们还会使浏览器的抽象层来负责事件的处理。

  首先,我们来定义BrowserAbstraction.js文件中的BrowserAbstraction()类,这个类提供了操作浏览器中DOM对象和事件处理程序的方法。由于每种浏览器都有自己的DOM,我们需要为支持的各种浏览器提供BrowserAbstraction()类的子类━━WindowsIEAbstraction()、MacNav4Abstraction()等,库文件BrowserDetect.js中定义了一个带有判断各种浏览器布尔属性(nav、ie、win)的BrowserDetect()类。

  其次,我们需要一种只下载与指定浏览器相关的代码的方法,在这里我们采用了“抽象工厂”模式,其中有一个“工厂”类来决定创建抽象超类(例如BrowserAbstraction())中具体的子类(例如WindowsIEAbstraction())。在我们的例子中,工厂类是BrowserAbstractionFactory(),该类的一个实例创建了BrowserDetect()对象,在这个对象的基础上该类建立了一个与指定浏览器的JS文件相连接的新的 元素,该JS文件中定义了相应浏览器的抽象类,比如,WindowsNavAbstraction.js文件定义了WindowsNavAbstraction()类。 一旦建立了BrowserAbstractionFactory()类,其getAbstraction方法将返回一个基于指定浏览器子类的BrowserAbstraction()对象。 (下略)

 

设计和具体的编码工作看起来有点复杂,但代码会被封装在组织得很好的模块中,每个类的功能也互不相关,维护起来会非常简单,整个系统也非常方便互联网网页作者使用。下面是一个简单的的例子,其中的classpath是包含着特定浏览器使用的JS文件的目录:


  建立被称作myFactory的BrowserAbstractionFactory()加载与指定浏览器相关的类,getAbstraction()方法在myBrowser对象中返回这个类的一个实例,这个实例将作为该浏览器DOM的通用接口。

  构件的设计

  设计出浏览器抽象层API后,我们就可以围绕它设计构件了,所有对浏览器DOM的调用都将通过BrowserAbstraction()对象来完成。我们可以通过构件的功能而不是它们所包含的HTML组件来描述构件,模式是逻辑性而非物理性的,而且构件的设计几乎不使用HTML的概念。

  有了上面的知识,我们就可以开始确定要创建的真实的对象的方法了,在我们的例子中,它们是一个滑动块和一个滚动条,我们可以通过确定它们应该具有的方法对这些对象进行模型化,下面是我们初步的类图表:

     


 

  下面需要做的是判别这些类之间的相似和不同之处。这两个类有5个相同的方法:draw()、setPosition()、activate()、deactivate()和move(),其中头两个方法的作用是实现HTML代码和设置构件在屏幕上的位置,其他的三个是浏览器抽象层所需要的事件处理程序。在这里就会用到继承的概念了:我们可以把这5个通用的方法放到一个新的超级类中。

  我们将这个超级类称为AbstractWidget(),因为它仅仅用于创建子类。Slider()、 Scrollbar()两个类都是由继承它而生成的,因此类的图表会变成如下所示:
(图3)

     

  最后是高级的设计特征。大多数的Slider()和Scrollbar()都具备的方法的执行效果并不相同,例如,每个子类的draw()方法输出不同的HTML代码。由于这个原因,除deactivate()之外的所有的AbstractWidget()方法都是抽象的方法,在我们下面所示的最终的类图表中,这四种方法在超级类和子类中都有定义:AbstractWidget定义了通用的、动作最少的操作,Slider和Scrollbar类则通过覆盖一些方法增加了相应的功能。

      



  AbstractWidget()超级类被安全地封装在AbstractWidget.js后,我们接下来需要做的就是设计可以实际工作的部件了,你会看到我们在设计阶段的艰苦的工作最终会从使用的方便性上得到回报。

简单的例子:易用性

  在有了抽象的AbstractWidget()类后,我们就可以设计一个具体的子类。在浏览器的抽象系统中,超级类BrowserAbstraction()仅仅是一个占位符,BrowserAbstractionFactory()的getAbstraction()方法返回一个指定浏览器的子类,例如WindowsNavAbstraction(),这里,我们就会真正地用到继承了。
我们需要把超级类定义为子类方法的基础,然后调用这些方法。我们首先定义Slider()子类:

function Slider(browserAbstractionLayer, instanceName)
{
  //Slider is a subclass of AbstractWidget
  this.base = AbstractWidget;
  this.base(browserAbstractionLayer, instanceName);
  ...


  因此当建立一个Slider()对象时,它用相同的参数对自己执行AbstractWidget()方法。

  Slider()类的其余部分使用这些参数定义一个起具体作用的Slider构件,browserAbstractionLayer参数传递一个在所有的DOM操作中使用的、以前创建的BrowserAbstraction()对象,instanceName用于生成和引用在构件的HTML代码中的ID。详细的Slider构件可以参阅Slider.js文件。

  好了,我们已经完成了“艰巨的”任务,让我们来看看得到的回报吧:在网页上使用构件变得如此地简单。

...(下略)

 

这段脚本建立了一个被称作mySlider的Slider()的实例,并执行其draw()方法输出滑动条的HTML代码,这个按钮把mySlider作为一个参数传给alertRandom()函数,alertRandom()函数调用该对象的getValue()方法生成一个1-100之间的随机数。

  扩展的例子:构件重用

  既然我们已经利用浏览器抽象层和一些构件建立了一个简单的应用,我们现在看看组件的重用问题。假设我们需要一个新的滑动块,它与我们已经创建的Slider()相似,但具有二个新的特性,第一,它不返回一个0-100之间的数字,而是根据其位置返回一个数组中的值,第二,当释放这个滑动块时,它会根据条目的编号停留在最近的条目上。

  我们把新的构件称作SelectionSlider(),原因是它与Slider()类的功能类似,使得它成为Slider()的一个子类。它的第一项新的特性描述了getValue()的一次新的执行,因此SelectionSlider()将会覆盖掉原来的方法。为了支持一系列可能的选择,SelectionSlider()还引入了一个新的方法setValues(),它接收一个新的数组,并把它们存储在一个被称作values的类变量中。

  第二个特性改变了鼠标释放时构件执行的操作,“mouse-up”事件激活deactivate()方法,因此我们需要覆盖掉这个方法并增加一个新的方法snap(),它在一个适当的位置重新画出滑块。下面是修正后的类的图表:
(图5)

  设计新的类将变得非常地简单,具体的编码工作也相当简单。新的SelectionSlider()继承了Slider()。

function SelectionSlider(browserAbstractionLayer, instanceName)
{
  // SelectionSlider is a subclass of Slider
  this.base = Slider;
  this.base(browserAbstractionLayer, instanceName);
  ...

  在结尾时我们重写了deactivate()让它调用snap(),重写getValue()以便计算出根据变量values中的值滑动块应该停留的位置。同样,我们编写snap()把滑动块的中心定在选定值上:

  this.deactivate = deactivateMethod;
  this.getValue = getValueMethod;
  this.snap = snapMethod;


  function deactivateMethod(x,y)
  {
    // this method was empty in the superclass.
    this.snap();
  }


  function getValueMethod()
  {
    var step = (this.sliderBarWidth - this.sliderKnobWidth + 1) / this.values.length;
    var selectedValue = this.values[Math.floor(this.sliderKnobLeft / step)];
    return selectedValue;
  }


  function snapMethod()
  {
    var step = (this.sliderBarWidth - this.sliderKnobWidth + 1) / this.values.length;
    var newPos = ( step * Math.floor(this.sliderKnobLeft / step) )+(.5 * step)
    this.browserAbstractionLayer.setDivLeft(this.sliderKnobDivObj, newPos);
    this.sliderKnobLeft = newPos;
  }


  最后,我们编写setValues()方法并增加values变量。

this.values = new Array();
this.setValues = setValuesMethod;
function setValuesMethod(arrayOfValues)
{
  this.values = arrayOfValues;
}


  这就是我们所要做的全部工作。由于仅仅需要编写支持新特性的代码,SelectionSlider.js文件显得非常简明,而且我们也仅仅需要了解需要改变的部分。在这里我们没有更多地关心如何“拖”滑动块。

 

总结

  我写这篇稿子的目的,是表明面向对象这一软件工程方法可以象使Java、C++软件开发人员受益那样使DHTML代码开发人员受益。JavaScript的功能不够强大的谣言应该是不攻自破了,虽然它的功能不如Java强大,但也决不是仅仅可以实现物体翻转和表格确认的小玩意儿。

  我们已经说明了如何通过这种方法实现组件的重用而无需重新编写代码,设计模式和抽象使得我们可以以很低的代价解决多种浏览器平台不兼容的问题,可以很方便地建立对象并在未来需要时对它们进行扩充,最终开发人员可以在对构件理解最少的情况下、以最少的编码工作量使用这些构件。Java中的Java Beans结构采用了相同的方法。

  面向对象带来的另一个优点是代码质量的提高,通过构建和测试基本的对象,可以建立一个经过严格测试的代码库和可靠的组件,从而无需在以后的使用中对每个组件重新测试,而仅需对组件的整体进行测试。这种方法有助于加快开发的进度。

  两个需要改进的问题。在性能方面,我们需要在良好的设计和快速的下载之间取得平衡。BrowserAbstractionFactory()仅下载特定浏览器相关的代码使得我们在下载代码的大小方面仍然有改进的余地,似乎把独立的小构件集成为一个大的复杂的构件效率会更高一些,因为太多的单独的JavaScript源文件反而会使效率降低,尽管浏览器会对它们进行缓冲,但似乎仍然有进行优化的余地。

  另一个可以改进的地方是事件的处理。BrowserAbstractionLayer()支持构件一级的事件处理而不支持子构件级的事件处理,命令模式可能会有助于我们找到一个更好的方法。

  在这篇文章中介绍的完整的浏览器抽象和构件库被整理成一个ZIP压缩文件,因此你可以对它进行测试、使用,或者在它们的基础上开发DHTML代码。我希望你已经得到了研究一种新的开发DHTML代码的方法。

关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有