iNeo

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  30 Posts :: 8 Stories :: 2 Comments :: 0 Trackbacks

2006年8月3日 #

我的tomcat工程建在:d:\eclipse\workspace\luc,索引文件是d:\eclipse\workspace\luc\public\index,建立索引的时候路径为"luc\\public\\index",但在搜索的时候,我用
searcher = new IndexSearcher(“luc\\public\\index”);
报错了,因为lucene在FSDirectory里将我的路径给new File(file.getCanonicalPath());
但奇怪的是file.getCanonicalPath()出来路径不对呀
错误是
java.io.IOException: D:\eclipse\luc\public\index not a directory
应该是d:\eclipse\workspace\luc\public\index 才对的
郁闷很久了,现在还没办法解决!!!!!!!!!!!!!!!!!!!!!!!!!!
posted @ 2006-08-03 09:12 只牵这只狗 阅读(321) | 评论 (0)编辑 收藏

2006年3月30日 #

今天上午升级 Eclipse 3.1.2 版本,完了之后就想找个数据库的插件,但花了近 2 个小时后得出的结论是:还没有支持 3.1.x 版本的数据库插件,郁闷的不行。看看 eclipse3.1.12 的发行日期—— 2005.12.26 ,咳,去年 12 月份出的,想不到到现在还没有支持的版本。当然也可能是我没找到。如果哪位兄台能给个支持 3.1.x 版本的数据库插件,将不甚感激!

虽然没找到正主,但还是找到了一些 Eclipse3.0.x 版本可用的版本,以共将来参考。

SQLExplorer
SQLExplorer 可以通过JDBC访问几乎任何一种数据库。同时也支持像Hibernate这样的工具访问数据库。现在的版本是SQLExporer 2.2.4 released

下栽地址:http://sourceforge.net/projects/eclipsesql

JFaceDBC

是一个很好的 SQL 控制台 , 支持各类主流数据库。现在的版本是 JFaceDbc 3.0

下栽地址:http://sourceforge.net/projects/jfacedbc

Quantum
Quantum 是一个数据库访问插件.它当前支持的数据库有:Postgres,MySQL,Adabas,DB2,Oracle与有JDBC驱动的数据库.它可以显示表格,视图并提供一个语法可高亮显示的SQL编辑器

com.quantum.feature_3.0.1.bin.dist

下栽地址:http://sourceforge.net/project/showfiles.php?group_id=7746&package_id=57047

posted @ 2006-03-30 12:42 只牵这只狗 阅读(3621) | 评论 (1)编辑 收藏

2006年3月15日 #

TMD..........

posted @ 2006-03-15 16:54 只牵这只狗 阅读(215) | 评论 (0)编辑 收藏

2006年1月17日 #

任何人都可以重构

使用 Eclipse 自动重构特性的方法与原因

 

 

未显示需要 JavaScript 的文档选项


对此页的评价

 


级别: 初级

David Gallardo, 独立软件顾问和作家

2003 年 11 月 10 日

Eclipse 提供了一组强大的自动重构(refactoring)功能,这些功能穿插在其他功能当中,使您能够重命名 Java元素,移动类和包,从具体的类中创建接口,将嵌套的类变成顶级类,以及从旧方法的代码片断中析取出新的方法。您熟悉了 Eclipse 的重构工具之后,就掌握了一种提高生产率的好方法。本文综览Eclipse 的重构特性,并通过例子阐明了使用这些特性的方法与原因。

为什么重构?

重构是指在不改变程序功能的前提下改变其结构。重构是一项功能强大的技术,但是执行起来需要倍加小心才行。主要的危险在于可能在不经意中引入一些错误,尤其是在进行手工重构的时候更是如此。这种危险引发了对重构技术的普遍批评:当代码不会崩溃的时候为什么要修改它呢?

您需要进行代码重构的原因可能有以下几个:传说中的第一个原因是:需要继承为某个古老产品而开发的年代久远的代码,或者突然碰到这些代码。最初的开发团队已经不在了。我们必须创建增加了新特性的新版本软件,但是这些代码已经无法理解了。新的开发队伍夜以继日地工作,破译代码然后映射代码,经过大量的规划与设计之后,人们将这些代码分割成碎片。历经重重磨难之后,所有这些东西都按照新版本的要求归位了。这是英雄般的重构故事,几乎没有人能在经历了这些之后活着讲述这样的故事。

还有一种现实一些的情况是项目中加入了新的需求,需要对设计进行修改。至于是因为在最初的规划过程中失察,还是由于采用了迭代式的开发过程(比如敏捷开发,或者是测试驱动的开发)而在开发过程中有意引入需求,这两者并没有实质性的区别。这样的重构的规模要小得多,其内容一般涉及通过引入接口或者抽象类来更改类的继承关系,以及对类进行分割和重新组织,等等。

重构的最后一个原因是,当存在可用的自动重构工具时,可以有一个用来预先生成代码的快捷方式——就好比在您无法确定如何拼写某个单词的时候,可以用某种拼写检查工具输入这个单词。比如说,您可以用这种平淡无奇的重构方法生成 getter 和 setter 方法,一旦熟悉了这样的工具,它就可以为您节省很多的时间。

Eclipse 的重构工具无意进行英雄级的重构——适合这种规模的工具几乎没有——但是不论是否用到敏捷开发技术,Eclipse 的工具对于一般程序员修改代码的工作都具有无法衡量的价值。毕竟任何复杂的操作只要能够自动进行,就可以不那么烦闷了。只要您知道 Eclipse 实现了什么样的重构工具,并理解了它们的适用情况,您的生产力就会得到极大的提高。

要降低对代码造成破坏的风险,有两种重要的方法。第一种方法是对代码进行一套完全彻底的单元测试:在重构之前和之后都必须通过这样的测试。第二种方法是使用自动化的工具来进行重构,比如说 Eclipse 的重构特性。

将彻底的测试与自动化重构结合起来就会更加有效了,这样重构也就从一种神秘的艺术变成了有用的日常工具。为了增加新的功能或者改进代码的可维护性,我们可以在不影响原有代码功能的基础上迅速且安全地改变其结构。这种能力会对您设计和开发代码的方式产生极大的影响,即便是您没有将其结合到正式的敏捷方法中也没有关系。



回页首


Eclipse 中重构的类型

Eclipse 的重构工具可以分为三大类(下面的顺序也就是这些工具在 Refactoring 菜单中出现的顺序):

  1. 对代码进行重命名以及改变代码的物理结构,包括对属性、变量、类以及接口重新命名,还有移动包和类等。
  2. 改变类一级的代码逻辑结构,包括将匿名类转变为嵌套类,将嵌套类转变为顶级类、根据具体的类创建接口,以及从一个类中将方法或者属性移到子类或者父类中。
  3. 改变一个类内部的代码,包括将局部变量变成类的属性、将某个方法中选中部分的代码变成一个独立的方法、以及为属性生成 getter 和 setter 方法。

还有几个重构工具并不能完全归入这三个种类,特别是 Change Method Signature,不过在本文中还是将这个工具归入第三类。除了这种例外情况以外,本文下面几节都是按照上面的顺序来讨论 Eclipse 重构工具的。



回页首


物理重组与重命名

显然,您即便没有特别的工具,也可以在文件系统中重命名文件或者是移动文件,但是如果操作对象是 Java 源代码文件,您就需要编辑很多文件,更新其中的 import package 语句。与此类似,用某种文本编辑器的搜索与替换功能也可以很容易地给类、方法和变量重新命名,但是这样做的时候必须十分小心,因为不同的类可能具有名称相似的方法或者变量;要是从头到尾检查项目中所有的文件,来保证每个东西的标识和修改的正确性,那可真够乏味的。

Eclipse 的 Rename 和 Move 工具能够十分聪明地在整个项目中完成这样的修改,而不需要用户的干涉。这是因为 Eclipse 可以理解代码的语义,从而能够识别出对某个特定方法、变量或者类名称的引用。简化这一任务有助于确保方法、变量和类的名称能够清晰地指示其用途。

我们经常可以发现代码的名字不恰当或者令人容易误解,这是因为代码与最初设计的功能有所不同。比方说,某个用来在文件中查找特定单词的程序也许会扩展为在 Web 页面中通过 URL 获取 InputStream 的操作。如果这一输入流最初叫做 file ,那么就应该修改它的名字,以便能反映其新增的更加一般的特性,比方说 sourceStream 。开发人员经常无法成功地修改这些名称,因为这个过程是十分混乱和乏味的。这当然也会把下一个不得不对这些类进行操作的开发人员弄糊涂。

要对某个 Java 元素进行重命名,只需要简单地从 Package Explorer 视图中点击这个元素,或者从Java 源代码文件中选中这个元素,然后选择菜单项 Refactor > Rename。在对话框中输入新的名称,然后选择是否需要 Eclipse 也改变对这个名称的引用。实际显示出来的确切内容与您所选元素的类型有关。比方说,如果选择的属性具有 getter 和 setter 方法,那么也就可以同时更新这些方法的名称,以反映新的属性。图1显示了一个简单的例子。

图 1. 重命名一个局部变量
Renaming a local variable

就像所有的 Eclipse 重构操作一样,当您指定了全部用来执行重构的必要信息之后,您就可以点击 Preview 按钮,然后在一个对话框中对比 Eclipse 打算进行哪些变更,您可以分别否决或者确认每一个受到影响的文件中的每一项变更。如果您对于 Eclipse 正确执行变更的能力有信心的话,您可以只按下 OK按钮。显然,如果您不确定重构到底做了什么事情,您就会想先预览一下,但是对于 Rename 和 Move 这样简单的重构而言,通常没有必要预览。

Move 操作与 Rename 十分相似:您选择某个 Java 元素(通常是一个类),为其指定一个新位置,并定义是否需要更新引用。然后,您可以选择 Preview检查变更情况,或者选择 OK 立即执行重构,如图2所示。

图 2. 将类从一个包移到另一个包
Moving a class

在某些平台上(特别是 Windows),您还可以在 Package Explorer 视图中通过简单拖放的方法将类从一个包或者文件夹中移到另一个包或文件夹中。所有的引用都会自动更新。



回页首


重新定义类的关系

Eclipse 中有大量的重构工具,使您能够自动改变类的关系。这些重构工具并没有 Eclipse 提供的其他工具那么常用,但是很有价值,因为它们能够执行非常复杂的任务。可以说,当它们用得上的时候,就会非常有用。

提升匿名类与嵌套类

Convert Anonymous Class(转换匿名类)和 Convert Nested Type(转换嵌套类)这两种重构方法比较相似,它们都将某个类从其当前范围移动到包含这个类的范围上。

匿名类是一种语法速写标记,使您能够在需要实现某个抽象类或者接口的地方创建一个类的实例,而不需要显式提供类的名称。比如在创建用户界面中的监听器时,就经常用到匿名类。在清单1中,假设 Bag 是在其他地方定义的一个接口,其中声明了两个方法, get() set()


清单 1. Bag 类
 
      public class BagExample
{
   void processMessage(String msg)
   {
      Bag bag = new Bag()
      {
         Object o;
         public Object get()
         {
            return o;
         }
         public void set(Object o)
         {
            this.o = o;
         }
      };
      bag.set(msg);
      MessagePipe pipe = new MessagePipe();
      pipe.send(bag);
   }
}

当匿名类变得很大,其中的代码难以阅读的时候,您就应该考虑将这个匿名类变成严格意义上的类;为了保持封装性(换句话说,就是将它隐藏起来,使得不必知道它的外部类不知道它),您应该将其变成嵌套类,而不是顶级类。您可以在这个匿名类的内部点击,然后选择 Refactor > Convert Anonymous Class to Nested 就可以了。当出现确认对话框的时候,为这个类输入名称,比如 BagImpl ,然后选择 Preview或者 OK。这样,代码就变成了如清单2所示的情形。


清单 2. 经过重构的 Bag 类
 

public class BagExample
{
   private final class BagImpl implements Bag
   {
      Object o;
      public Object get()
      {
         return o;
      }
      public void set(Object o)
      {
         this.o = o;
      }
   }
       
   void processMessage(String msg)
   {
     Bag bag = new BagImpl();
     bag.set(msg);
     MessagePipe pipe = new MessagePipe();
     pipe.send(bag);
   }
}

当您想让其他的类使用某个嵌套类时,Convert Nested Type to Top Level 就很有用了。比方说,您可以在一个类中使用值对象,就像上面的 BagImpl 类那样。如果您后来又决定应该在多个类之间共享这个数据,那么重构操作就能从这个嵌套类中创建新的类文件。您可以在源代码文件中高亮选中类名称(或者在 Outline 视图中点击类的名称),然后选择 Refactor > Convert Nested Type to Top Level,这样就实现了重构。

这种重构要求您为装入实例提供一个名字。重构工具也会提供建议的名称,比如 example ,您可以接受这个名字。这个名字的意思过一会儿就清楚了。点击 OK 之后,外层类 BagExample 就会变成清单3所示的样子。


清单 3. 经过重构的 Bag 类

public class BagExample
{
   void processMessage(String msg)
   {
      Bag bag = new BagImpl(this);
      bag.set(msg);
      MessagePipe pipe = new MessagePipe();
      pipe.send(bag);
   }
}

请注意,当一个类是嵌套类的时候,它可以访问其外层类的成员。为了保留这种功能,重构过程将一个装入类 BagExample 的实例放在前面那个嵌套类中。这就是之前要求您输入名称的实例变量。同时也创建了用于设置这个实例变量的构造函数。重构过程创建的新类 BagImpl 如清单4所示。


清单 4. BagImpl 类
  
final class BagImpl implements Bag
{
   private final BagExample example;
   /**
    * @paramBagExample
    */
  BagImpl(BagExample example)
   {
      this.example = example;
      // TODO Auto-generated constructor stub
   }
   Object o;
   public Object get()
   {
      return o;
   }
   public void set(Object o)
   {
      this.o = o;
   }
}

如果您的情况与这个例子相同,不需要保留对 BagExample 的访问,您也可以很安全地删除这个实例变量与构造函数,将 BagExample 类中的代码改成缺省的无参数构造函数。

在类继承关系内移动成员

还有两个重构工具,Push Down 和 Pull Up,分别实现将类方法或者属性从一个类移动到其子类或父类中。假设您有一个名为 Vehicle 的抽象类,其定义如清单5所示。


清单 5. 抽象的 Vehicle 类

public abstract class Vehicle
{
   protected int passengers;
   protected String motor;
   
   public int getPassengers()
   {
      return passengers;
   }
   public void setPassengers(int i)
   {
      passengers = i;
   }
   public String getMotor()
   {
      return motor;
   }
   public void setMotor(String string)
   {
      motor = string;
   }
}

您还有一个 Vehicle 的子类,类名为 Automobile ,如清单6所示。


清单6. Automobile 类

public class Automobile extends Vehicle
{
   private String make;
   private String model;
   public String getMake()
   {
      return make;
   }
   public String getModel()
   {
      return model;
   }
   public void setMake(String string)
   {
      make = string;
   }
   public void setModel(String string)
   {
      model = string;
   }
}

请注意, Vehicle 有一个属性是 motor 。如果您知道您将永远只处理汽车,那么这样做就好了;但是如果您也允许出现划艇之类的东西,那么您就需要将 motor 属性从 Vehicle 类下放到 Automobile 类中。为此,您可以在 Outline 视图中选择 motor ,然后选择 Refactor > Push Down

Eclipse 还是挺聪明的,它知道您不可能总是单单移动某个属性本身,因此还提供了 Add Required 按钮,不过在 Eclipse 2.1 中,这个功能并不总是能正确地工作。您需要验证一下,看所有依赖于这个属性的方法是否都推到了下一层。在本例中,这样的方法有两个,即与 motor 相伴的 getter 和 setter 方法,如图3所示。

图 3. 加入所需的成员
Adding required members

在按过 OK按钮之后, motor 属性以及 getMotor() setMotor() 方法就会移动到 Automobile 类中。清单7显示了在进行了这次重构之后 Automobile 类的情形。


清单 7. 经过重构的 Automobile 类
public class Automobile extends Vehicle
{
   private String make;
   private String model;
   protected String motor;
   public String getMake()
   {
      return make;
   }
   public String getModel()
   {
      return model;
   }
   public void setMake(String string)
   {
      make = string;
   }
   public void setModel(String string)
   {
      model = string;
   }
   public String getMotor()
   {
      return motor;
   }
   public void setMotor(String string)
   {
      motor = string;
   }
}

Pull Up 重构与 Push Down 几乎相同,当然 Pull Up 是将类成员从一个类中移到其父类中,而不是子类中。如果您稍后改变主意,决定还是把 motor 移回到 Vehicle 类中,那么您也许就会用到这种重构。同样需要提醒您,一定要确认您是否选择了所有必需的成员。

Automobile 类中具有成员 motor,这意味着您如果创建另一个子类,比方说 Bus ,您就还需要将 motor (及其相关方法)加入到 Bus 类中。有一种方法可以表示这种关系,即创建一个名为 Motorized 的接口, AutomobileBus 都实现这个接口,但是 RowBoat 不实现。

创建 Motorized 接口最简单的方法是在 Automobile 上使用 Extract Interface 重构。为此,您可以在 Outline 视图中选择 Automobile ,然后从菜单中选择 Refactor > Extract Interface。您可以在弹出的对话框中选择您希望在接口中包含哪些方法,如图4所示。

图 4. 提取 Motorized 接口
Motorized interface

点击 OK 之后,接口就创建好了,如清单8所示。


清单 8. Motorized 接口

public interface Motorized
{
   public abstract String getMotor();
   public abstract void setMotor(String string);
}

同时, Automobile 的类声明也变成了下面的样子:


public class Automobile extends Vehicle implements Motorized

使用父类

本重构工具类型中最后一个是 User Supertyp Where Possible。想象一个用来管理汽车细帐的应用程序。它自始至终都使用 Automobile 类型的对象。如果您想处理所有类型的交通工具,那么您就可以用这种重构将所有对 Automobile 的引用都变成对 Vehicle 的引用(参看图5)。如果您在代码中用 instanceof 操作执行了任何类型检查的话,您将需要决定在这些地方适用的是原先的类还是父类,然后选中第一个选项“Use the selected supertype in 'instanceof' expressions”。

图 5. 将 Automobile 改成其父类 Vehicle
Supertype

使用父类的需求在 Java 语言中经常出现,特别是在使用了 Factory Method 模式的情况下。这种模式的典型实现方式是创建一个抽象类,其中具有静态方法 create() ,这个方法返回的是实现了这个抽象类的一个具体对象。如果需创建的具体对象的类型依赖于实现的细节,而调用类对实现细节并不感兴趣的情况下,可以使用这一模式。



回页首


改变类内部的代码

最大一类重构是实现了类内部代码重组的重构方法。在所有的重构方法中,只有这类方法允许您引入或者移除中间变量,根据原有方法中的部分代码创建新方法,以及为属性创建 getter 和 setter 方法。

提取与内嵌

有一些重构方法是以 Extract 这个词开头的:Extract Method、Extract Local Variable 以及Extract Constants。第一个 Extract Method 的意思您可能已经猜到了,它根据您选中的代码创建新的方法。我们以清单8中那个类的 main() 方法为例。它首先取得命令行选项的值,如果有以 -D 开头的选项,就将其以名-值对的形式存储在一个 Properties 对象中。


清单 8. main()

import java.util.Properties;
import java.util.StringTokenizer;
public class StartApp
{
   public static void main(String[] args)
   {
      Properties props = new Properties();
      for (int i= 0; i < args.length; i++)
      {
         if(args[i].startsWith("-D"))
         {
           String s = args[i].substring(2);
           StringTokenizer st = new StringTokenizer(s, "=");
            if(st.countTokens() == 2)
            {
              props.setProperty(st.nextToken(), st.nextToken());
            }
         }
      }
      //continue...
   }
}

将一部分代码从一个方法中取出并放进另一个方法中的原因主要有两种。第一种原因是这个方法太长,并且完成了两个以上逻辑上截然不同的操作。(我们不知道上面那个 main() 方法还要处理哪些东西,但是从现在掌握的证据来看,这不是从其中提取出一个方法的理由。)另一种原因是有一段逻辑上清晰的代码,这段代码可以被其他方法重用。比方说在某些时候,您发现自己在很多不同的方法中都重复编写了相同的几行代码。那就有可能是需要重构的原因了,不过除非真的需要重用这部分代码,否则您很可能并不会执行重构。

假设您还需要在另外一个地方解析名-值对,并将其放在 Properties 对象中,那么您可以将包含 StringTokenizer 声明和下面的 if 语句的这段代码抽取出来。为此,您可以高亮选中这段代码,然后从菜单中选择 Refactor > Extract Method。您需要输入方法名称,这里输入 addProperty ,然后验证这个方法的两个参数, Properties prop Strings 。清单9显示由 Eclipse 提取了 addProp() 方法之后类的情况。


清单 9. 提取出来的 addProp()

import java.util.Properties;
import java.util.StringTokenizer;
public class Extract
{
   public static void main(String[] args)
   {
      Properties props = new Properties();
      for (int i = 0; i < args.length; i++)
      {
         if (args[i].startsWith("-D"))
         {
            String s = args[i].substring(2);
            addProp(props, s);
         }
      }
   }
   private static void addProp(Properties props, String s)
   {
      StringTokenizer st = new StringTokenizer(s, "=");
      if (st.countTokens() == 2)
      {
         props.setProperty(st.nextToken(), st.nextToken());
      }
   }
}

Extract Local Variable 重构取出一段被直接使用的表达式,然后将这个表达式首先赋值给一个局部变量。然后在原先使用那个表达式的地方使用这个变量。比方说,在上面的方法中,您可以高亮选中对 st.nextToken() 的第一次调用,然后选择 Refactor > Extract Local Variable。您将被提示输入一个变量名称,这里输入 key 。请注意,这里有一个将被选中表达式所有出现的地方都替换成新变量的引用的选项。这个选项通常是适用的,但是对这里的 nextToken() 方法不适用,因为这个方法(显然)在每一次调用的时候都返回不同的值。确认这个选项未被选中。参见图6。

图 6. 不全部替换所选的表达式
Extract variable

接下来,在第二次调用 st.nextToken() 的地方重复进行重构,这一次调用的是一个新的局部变量 value 。清单10显示了这两次重构之后代码的情形。


清单 10. 重构之后的代码

private static void addProp(Properties props, String s)
   {
     StringTokenizer st = new StringTokenizer(s, "=");
      if(st.countTokens() == 2)
      {
         String key = st.nextToken();
         String value = st.nextToken();
        props.setProperty(key, value);
      }
   }

用这种方式引入变量有几点好处。首先,通过为表达式提供有意义的名称,可以使得代码执行的任务更加清晰。第二,代码调试变得更容易,因为我们可以很容易地检查表达式返回的值。最后,在可以用一个变量替换同一表达式的多个实例的情况下,效率将大大提高。

Extract Constant 与 Extract Local Variable 相似,但是您必须选择静态常量表达式,重构工具将会把它转换成静态的 final 常量。这在将硬编码的数字和字符串从代码中去除的时候非常有用。比方说,在上面的代码中我们用“-D”这一命令行选项来定义名-值对。先将“-D”高亮选中,选择 Refactor > Extract Constant,然后输入 DEFINE 作为常量的名称。重构之后的代码如清单11所示:


清单 11. 重构之后的代码

public class Extract
{
   private static final String DEFINE = "-D";
   public static void main(String[] args)
   {
      Properties props = new Properties();
      for (int i = 0; i < args.length; i++)
      {
         if (args[i].startsWith(DEFINE))
         {
            String s = args[i].substring(2);
            addProp(props, s);
         }
      }
   }
   // ...

对于每一种 Extract... 类的重构,都存在对应的 Inline... 重构,执行与之相反的操作。比方说,如果您高亮选中上面代码中的变量 s,选择 Refactor > Inline...,然后点击 OK,Eclipse 就会在调用 addProp() 的时候直接使用 args[i].substring(2) 这个表达式,如下所示:

        if(args[i].startsWith(DEFINE))
         {
            addProp(props,args[i].substring(2));
         }

这样比使用临时变量效率更高,代码也变得更加简要,至于这样的代码是易读还是含混,就取决于您的观点了。不过一般说来,这样的内嵌重构没什么值得推荐的地方。

您可以按照用内嵌表达式替换变量的相同方法,高亮选中方法名,或者静态 final 常量,然后从菜单中选择 Refactor > Inline...,Eclipse 就会用方法的代码替换方法调用,或者用常量的值替换对常量的引用。

封装属性

通常我们认为将对象的内部结构暴露出来是一种不好的做法。这也正是 Vehicle 类及其子类都具有 private 或者 protected 属性,而用 public setter 和 getter 方法来访问属性的原因。这些方法可以用两种不同的方式自动生成。

第一种生成这些方法的方式是使用 Source > Generate Getter and Setter 菜单。这将会显示一个对话框,其中包含所有尚未存在的 getter 和 setter 方法。不过因为这种方式没有用新方法更新对这些属性的引用,所以并不算是重构;必要的时候,您必须自己完成更新引用的工作。这种方式可以节约很多时间,但是最好是在一开始创建类的时候,或者是向类中加入新属性的时候使用,因为这些时候还不存在对属性的引用,所以不需要再修改其他代码。

第二种生成 getter 和 setter 方法的方式是选中某个属性,然后从菜单中选择 Refactor > Encapsulate Field。这种方式一次只能为一个属性生成 getter 和 setter 方法,不过它与 Source > Generate Getter and Setter 相反,可以将对这个属性的引用改变成对新方法的调用。

例如,我们可以先创建一个新的简版 Automobile 类,如清单12所示。


清单 12. 简单的 Automobile 类

public class Automobile extends Vehicle
{
   public String make;
   public String model;
}

接下来,创建一个类实例化了 Automobile 的类,并直接访问 make 属性,如清单13所示。


清单 13. 实例化 Automobile

public class AutomobileTest
{
   public void race()
   {
      Automobilecar1 = new Automobile();
      car1.make= "Austin Healy";
      car1.model= "Sprite";
      // ...
   }
}

现在封装 make 属性。先高亮选中属性名称,然后选择 Refactor > Encapsulate Field。在弹出的对话框中输入 getter 和 setter 方法的名称——如您所料,缺省的方法名称分别是 getMake() 和 setMake()。您也可以选择与这个属性处在同一个类中的方法是继续直接访问该属性,还是像其他类那样改用这些访问方法。(有一些人非常倾向于使用这两种方式的某一种,不过碰巧在这种情况下您选择哪一种方式都没有区别,因为 Automobile 中没有对 make 属性的引用。)

图7. 封装属性
Encapsulating a field

点击 OK之后, Automobile 类中的 make 属性就变成了私有属性,也同时具有了 getMake() setMake() 方法。

>
清单 14. 经过重构的 Automobile 类
public class Automobile extends Vehicle
{
   private String make;
   public String model;

   public void setMake(String make)
   {
      this.make = make;
   }

   public String getMake()
   {
      return make;
   }
}

AutomobileTest 类也要进行更新,以便使用新的访问方法,如清单15所示。


>清单 15. AutomobileTest 类

public class AutomobileTest
{
   public void race()
   {
      Automobilecar1 = new Automobile();
      car1.setMake("Austin Healy");
      car1.model= "Sprite";
      // ...
   }
}

改变方法的签名

本文介绍的最后一个重构方法也是最难以使用的方法:Change Method Signature(改变方法的签名)。这种方法的功能显而易见——改变方法的参数、可见性以及返回值的类型。而进行这样的改变对于调用这个方法的其他方法或者代码会产生什么影响,就不是那么显而易见了。这么也没有什么魔方。如果代码的改变在被重构的方法内部引发了问题——变量未定义,或者类型不匹配——重构操作将对这些问题进行标记。您可以选择是接受重构,稍后改正这些问题,还是取消重构。如果这种重构在其他的方法中引发问题,就直接忽略这些问题,您必须在重构之后亲自修改。

为澄清这一点,考虑清单16中列出的类和方法。


清单 16. MethodSigExample 类

public class MethodSigExample
{
   public int test(String s, int i)
   {
      int x = i + s.length();
      return x;
   }
}

上面这个类中的 test() 方法被另一个类中的方法调用,如清单17所示。


清单 17. callTest 方法

public void callTest()
   {
     MethodSigExample eg = new MethodSigExample();
     int r = eg.test("hello", 10);
   }

在第一个类中高亮选中 test ,然后选择 Refactor > Change Method Signature。您将看到如图8所示的对话框。

图 8. Change Method Signature 选项
Change Method Signature options

第一个选项是改变该方法的可见性。在本例中,将其改变为 protected 或者 private,这样第二个类的 callTest() 方法就不能访问这个方法了。(如果这两个类在不同的包中,将访问方法设为缺省值也会引起这样的问题。) Eclipse 在进行重构的时候不会将这些问题标出,您只有自己选择适当的值。

下面一个选项是改变返回值类型。如果将返回值改为 float ,这不会被标记成错误,因为 test() 方法返回语句中的 int 会自动转换成 float 。即便如此,在第二个类的 callTest() 方法中也会引起问题,因为 float 不能转换成 int 。您需要将 test() 的返回值改为 int ,或者是将 callTest() 中的 r 改为 float

如果将第一个参数的类型从 String 变成 int ,那么也得考虑相同的问题。在重构的过程中这些问题将会被标出,因为它们会在被重构的方法内部引起问题: int 不具有方法 length() 。然而如果将其变成 StringBuffer ,问题就不会标记出来,因为 StringBuffer 的确具有方法 length() 。当然这会在 callTest() 方法中引起问题,因为它在调用 test() 的时候还是把一个 String 传递进去了。

前面提到过,在重构引发了问题的情况下,不管问题是否被标出,您都可以一个一个地修正这些问题,以继续下去。还有一种方法,就是先行修改这些错误。如果您打算删除不再需要的参数 i ,那么可以先从要进行重构的方法中删除对它的引用。这样删除参数的过程就更加顺利了。

最后一件需要解释的事情是 Default Value 选项。这一选项值仅适用于将参数加入方法签名中的情况。比方说,如果我们加入了一个类型为 String 的参数,参数名为 n ,其缺省值为 world ,那么在 callTest() 方法中调用 test() 的代码就变成下面的样子:

      
         public void callTest()
   {
      MethodSigExample eg = new MethodSigExample();
      int r = eg.test("hello", 10, "world");
   }

在这场有关 Change Method Signature 重构的看似可怕的讨论中,我们并没有隐藏其中的问题,但却一直没有提到,这种重构其实是非常强大的工具,它可以节约很多时间,通常您必须进行仔细的计划才能成功地使用它。



回页首


结束语

Eclipse 提供的工具使重构变得简单,熟悉这些工具将有助于您提高效率。敏捷开发方法采用迭代方式增加程序特性,因此需要依赖于重构技术来改变和扩展程序的设计。但即便您并没有使用要求进行正式重构的方法,Eclipse 的重构工具还是可以在进行一般的代码修改时提供节约时间的方法。如果您花些时间熟悉这些工具,那么当出现可以利用它们的情况时,您就能意识到所花费的时间是值得的。



回页首


参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.

  • 有关重构的核心著作是 Refactoring: Improving the Design of Existing Code, 作者 Martin Fowler、Kent Beck、John Brant、William Opdyke 和 Don Roberts(Addison-Wesley,1999年)。

  • 重构是一种正在发展的方法,在 Eclipse In Action: A Guide for Java Developers (Manning, 2003年)一书中,作者 David Gallardo,Ed Burnette 以及 Robert McGovern 从在 Eclipse 中设计和开发项目的角度讨论了这一话题。

  • 模式(如本文中提到的 Factory Method 模式)是理解和讨论面向对象设计的重要工具。这方面的经典著作是 Design Patterns: Elements of Reusable Object-Oriented Software,作者为 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides (Addison-Wesley,1995年)。

  • Design Patterns 中的例子是用 C++ 写成的,这对于 Java 程序员是不小的障碍;Mark Grand 所著的 Patterns in Java, Volume One: A Catalog of Reusable Design Patterns Illustrated with UML(Wiley,1998年)将模式翻译成了 Java 语言。

  • 有关敏捷编程的一个变种,请参看 Kent Beck 所著的 Extreme Programming Explained: Embrace Change(Addison-Wesley,1999年)
Web 站点 developerWorks 上的文章与教程

回页首


关于作者

David Gallardo 是 Studio B 上的一名作家,他是一名独立软件顾问和作家,专长为软件国际化、Java Web 应用程序和数据库开发。他成为专业软件工程师已经有十五年了,他拥有许多操作系统、编程语言和网络协议的经验。他最近在一家 BtoB 电子商务公司 TradeAccess, Inc 领导数据库和国际化开发。在这之前,他是 Lotus Development Corporation 的 International Product Development 组中的高级工程师,负责为 Lotus 产品(包括 Domino)提供 Unicode 和国际语言支持的跨平台库的开发。David 是 Eclipse In Action: A Guide for Java Developers(2003年)一书的合著者。可以通过 david@gallardo.org 与 David 联系。

posted @ 2006-01-17 15:28 只牵这只狗 阅读(324) | 评论 (0)编辑 收藏

2006年1月10日 #

原来自己的生日已经过了......不知不觉的过了...........
posted @ 2006-01-10 09:29 只牵这只狗 阅读(198) | 评论 (0)编辑 收藏

男人25岁前不会明白的道理

    

  1、男人是社会的主体,不管你信或不信。所以男人应该有种责任感。
   
  2、25岁之前,请记得,爱情通常是假的,或者不是你所想象的那样纯洁和永远。如果你过了25岁,那么你应该懂得这个道理。
   
  3、吃饭7成饱最舒服。对待女友最多也请你保持在7成。
   
  4、30岁之前请爱惜自己的身体,前30年你找病,后30年病找你。如果你过了30岁,你自然也会懂得这个道理。
   
  5、事业远比爱情重要。如果说事业都不能永恒,那么爱情只能算是昙花一现。
   
  6、不要轻易接受追求你的女孩。女追男隔层纱,如果你很容易就陷进去,你会发现你会错过很多东西,失去很多东西。
   
  7、请你相信,能用钱解决的问题,都不是问题。如果你认为钱索王道,有钱有女人,没钱没女人,那么,女人不是问题。
   
  8、请永远积极向上。每个男人都有他可爱的地方,但是不可爱的地方只有:不积极面对生活。
   
  9、不要连续2次让同一个女人受到伤害。好马不吃回头草是有道理的。如果认真考虑过该分手,那么请不要做任何舍不得的行动。
   
  10、如果你和你前女友能做朋友,那么你要问自己:为什么?如果分手后还是朋友,那么只有2个可能:你们当初都只是玩玩而已,没付出彼此最真的感情;或者:必定有个人是在默默的付出无怨无悔!
   
  11、永远不要太相信女人在恋爱时的甜言蜜语。都说女人爱听甜言蜜语,其实,男人更喜欢。
   
  12、请不要为自己的相貌或者身高过分担心和自卑。人是动物,但是区别于动物。先天条件并不是阻挡你好好生活的借口。人的心灵远胜于相貌,请相信这点。如果有人以相貌取人,那么你也没必要太在意。因为他从某种意义来讲,只是只动物,你会跟动物怄气吗?
   
  13、失恋时,只有2种可能,要么你爱她她不爱你,或者相反。那么,当你爱的人不再爱你,或者从来没爱过你时,你没有遗憾,因为你失去的只是一个不爱你的人。
   
  14、请不要欺骗善良的女孩。这个世界上善良的女孩太少。
   
  15、不能偏激的认为金钱万能,至少,金钱治不好艾滋病。
   
  16、请一定要有自信。你就是一道风景,没必要在别人风景里面仰视。
   
  17、受到再大的打击,只要生命还在,请相信每天的太阳都是新的。
   
  18、爱情永远不可能是天平。你想在爱情里幸福就要舍得伤心。
   
  19、如果你喜欢一个她认为别人应该对她好的MM,请尽早放弃。没有人是应该对一个人好的。如果她不明白这个道理,也就是她根本不懂得珍惜。
   
  20、不要因为寂寞而“找”GF,寂寞的男人请要学会品味寂寞。请记住:即使寂寞,远方黑暗的夜空下,一定有人和你一样,寂寞的人不同,仰望的星空却是唯一。
   
  21、任何事没有永远,也别问怎样才能永远。生活有很多无奈,请尽量充实自己,充实生活。请善待生活。
   
  End、男人有很多无奈,生活很累,但是因为生活才有意义。当你以为你一无所有时,你至少还有时间,时间能抚平一切创伤。所以请不要流泪。

posted @ 2006-01-10 09:12 只牵这只狗 阅读(228) | 评论 (0)编辑 收藏

也许明天会更好.......

posted @ 2006-01-10 08:51 只牵这只狗 阅读(213) | 评论 (0)编辑 收藏

2006年1月4日 #

摘要:

虽然大部分开发人员在过去使用过XMLHttp或者使用Iframe来加载数据,但仅到现在我们才看到传统的开发人员和公司开始采用这些技术。就像新的编程语言或模型伴随着更多的痛苦,开发人员需要学习新的技巧及如何最好利用这些新技术。这篇文章讲述了开发人员使用AJAX需要使用的工具和技术。

文章工具

作者:Andrew Thompson;xMatrix (他的blog:http://blog.matrix.org.cn/page/xMatrix)
原文地址:http://www.javaworld.com/javaworld/jw-10-2005/jw-1017-ajax.html
中文地址:http://www.matrix.org.cn/resource/article/44/44116_AJAX.html
关键词: AJAX

AJAX开发者的最新工具和技术

基于XML的异步JavaScript,简称AJAX,是当前Web创新(称为Web2.0)中的一个王冠。感谢组成AJAX的各种技术,Web应用的交互如Flickr, Backpack和Google在这方面已经有质的飞跃。这个术语源自描述从基于网页的Web应用到基于数据的应用的转换。在基于数据的应用中,用户需求的数据如联系人列表,可以从独立于实际网页的服务端取得并且可以被动态地写入网页中,给缓慢的Web应用体验着色使之像桌面应用一样。

虽然大部分开发人员在过去使用过XMLHttp或者使用Iframe来加载数据,但仅到现在我们才看到传统的开发人员和公司开始采用这些技术。就像新的编程语言或模型伴随着更多的痛苦,开发人员需要学习新的技巧及如何最好利用这些新技术。这篇文章讲述了开发人员使用AJAX枰褂玫墓ぞ吆图际酢?lt;br />
AJAX模式
许多重要的技术和AJAX开发模式可以从现有的知识中获取。例如,在一个发送请求到服务端的应用中,必须包含请求顺序、优先级、超时响应、错误处理及回调,其中许多元素已经在Web服务中包含了,就像现在的SOA。AJAX开发人员拥有一个完整的系统架构知识。同时,随着技术的成熟还会有许多地方需要改进,特别是UI部分的易用性。

AJAX开发与传统的CS开发有很大的不同。这些不同引入了新的编程问题,最大的问题在于易用性。由于AJAX依赖浏览器的JavaScript和XML,浏览器的兼容性和支持的标准也变得和JavaScript的运行时性能一样重要了。这些问题中的大部分来源于浏览器、服务器和技术的组合,因此必须理解如何才能最好的使用这些技术。

综合各种变化的技术和强耦合的客户服务端环境,AJAX提出了一种新的开发方式。AJAX开发人员必须理解传统的MVC架构,这限制了应用层次之间的边界。同时,开发人员还需要考虑CS环境的外部和使用AJAX技术来重定型MVC边界。最重要的是,AJAX开发人员必须禁止以页面集合的方式来考虑Web应用而需要将其认为是单个页面。一旦UI设计与服务架构之间的范围被严格区分开来后,开发人员就需要更新和变化的技术集合了。


时刻想着用户
AJAX的最大机遇在于用户体验。在使应用更快响应和创新的过程中,定义Web应用的规则正在被重写;因此开发人员必须更注重用户。现在用户已经逐渐习惯如何使用Web应用了。例如用户通常希望每一次按钮点击会导致几秒的延迟和屏幕刷新,但AJAX正在打破这种长时间的状况。因此用户需要重新体验按钮点击的响应了。

可用性是AJAX另人激动的地方而且已经产生了几种新颖的技术。其中最引人注目的是一种称为“黄色隐出”的技术,他在数据更新之前时将用户界面变为黄色,更新完成后立刻恢复原来的颜色。AJAX开发人员将用户从Web应用的负载中解放出来;小心地利用AJAX提供的丰富接口,不久桌面开发人员会发现AJAX是他们的方向。


几种工具和技术
随着AJAX迅速地引人注目起来,我想开发人员对这种技术的期待也迅速地增加。就像任何新技术,AJAX的兴旺也需要一整个开发工具/编程语言及相关技术系统来支撑。

JavaScript
如名字所示AJAX的概念中最重要而最被忽视的是他也是一种JavaScript编程语言。JavaScript是一种粘合剂使AJAX应用的各部分集成在一起。在大部分时间,JavaScript通常被服务端开发人员认为是一种企业级应用不需要使用的东西应该尽力避免。这种观点来来自以前编写JavaScript代码的经历:繁杂而又易出错的语言。类似的,他也被认为将应用逻辑任意地散布在服务端和客户端中,这使得问题很难被发现而且代码很难重用。在AJAX中JavaScript主要被用来传递用户界面上的数据到服务端并返回结果。XMLHttpRequest对象用来响应通过HTTP传递的数据,一旦数据返回到客户端就可以立刻使用DOM将数据放到网面上。

XMLHttpRequest
XMLHttpRequest对象在大部分浏览器上已经实现而且拥有一个简单的接口允许数据从客户端传递到服务端,但并不会打断用户当前的操作。使用XMLHttpRequest传送的数据可以是任何格式,虽然从名字上建议是XML格式的数据。

开发人员应该已经熟悉了许多其他XML相关的技术。XPath可以访问XML文档中的数据,但理解XML DOM是必须的。类似的,XSLT是最简单而快速的从XML数据生成HTML或XML的方式。许多开发人员已经熟悉Xpath和XSLT,因此AJAX选择XML作为数据交换格式有意义的。XSLT可以被用在客户端和服务端,他能够减少大量的用JavaScript编写的应用逻辑。

CSS
为了正确的浏览AJAX应用,CSS是一种AJAX开发人员所需要的重要武器。CSS提供了从内容中分离应用样式和设计的机制。虽然CSS在AJAX应用中扮演至关重要的角色,但他也是构建创建跨浏览器应用的一大阻碍,因为不同的浏览器厂商支持各种不同的CSS级别。

服务器端
但不像在客户端,在服务端AJAX应用还是使用建立在如Java,.Net和PHP语言基础上机制;并没有改变这个领域中的主要方式。
既然如此,我们对Ruby on Rails框架的兴趣也就迅速增加了。在一年多前,Ruby on Rails已经吸引了大量开发人员基于其强大功能来构建Web和AJAX应用。虽然目前还有很多快速应用开发工具存在,Ruby on Rails看起来已经储备了简化构建AJAX应用的能力。

开发工具
在实际构建AJAX应用中,你需要的不只是文本编辑器。既然是JavaScript非编译的,他可以容易地编写和运行在浏览器中;然而,许多工具提供了有用的扩展如语法高亮和智能完成。

不同的IDE提供了对JavaScript支持的不同等级。来自JetBrains的IntelliJ IDEA是一个用来JavaScript开发的更好的IDE,虽然许多开发人员也喜欢Microsoft’s Visual Studio产品(允诺会在最新的版本中改善对AJAX的支持)。Eclipse包含了两个免费的JavaScript编辑器插件和一个商业的来自ActiveStat的Komodo IDE。

另一个JavaScript和AJAX开发中的问题是调试困难。不同的浏览器提供不同的通常是隐藏的运行时错误信息,而JavaScript的缺陷如双重变量赋值(通常是由于缺少数据类型)使得调试更加困难。在AJAX的开发中,调试就更复杂了,因为其需要标识究竟是客户端还是服务端产生的错误。在过去,JavaScript调试的方法是删除所有代码然后一行行的增加直到错误出现。现在,更多开发人员回到为IE准备的Microsoft Script Debugger和为Mozilla浏览器准备的Venkman。


浏览器兼容性
JavaScript编程的最大问题来自不同的浏览器对各种技术和标准的支持。构建一个运行在不同浏览器(如IE和火狐)是一个困难的任务。因此几种AJAX JavaScript框架或者生成基于服务端逻辑或标记库的JavaScript,或者提供符合跨浏览器AJAX开发的客户端JavaScript库。一些流行的框架包括:AJAX.Net, Backbase, Bitkraft, Django, DOJO, DWR, MochiKit, Prototype, Rico, Sajax, Sarissa, and Script.aculo.us.

这些框架给开发人员更多的空间使得他们不需要担心跨浏览器的问题。虽然这些框架提升了开发人员构建应用的能力,但由于厂商已经开发了更细节的用户界面的打包组件解决方案,因此在AJAX组件市场中需要考虑一些其他因素。例如提供通用用户界面的组件如组合框和数据栅格的几个厂商,都可以被用来在应用中创建良好的通过类似电子数据表方式来查看和编辑数据的体验。但这些组件不仅是封装了组件的用户界面而且包括与服务端数据的通讯方式,这些组件通常使用基于标记方式来实现如ASP.Net或JSF控件。


展望
最近IE和火狐之间的浏览器之争变得火热起来,因此AJAX开发人员需要足够敏捷的作出反应。关键点在一些问题如CSS或XML,虽然各种浏览器形成采用最新标准的不同阵营(如Mozilla拥抱SVG和E4X标准及在最新火狐BETA版本中使用XUL,而微软使用自己的XAML技术)。所有这些技术代表当前AJAX主流JavaScript和XML的市场方向改变。
总的来说,AJAX开发人员必须尽快地跟进最新的技术并利用高产的工具集。成功的AJAX开发人员还需要留心他们的使用者以避免将任何问题扩大化。并且AJAX开发人员还需要持续地创新来创建增强Web应用易用性的新方法。


作者
Dave Johnson是加拿大Vancouver一家软件咨询公司eBusiness Applications创始人和传道者,拥有七年以上的XML相关工作经验。


Resources
• AJAX开发人员的编译模式:http://www.ajaxpatterns.org
•  XMLHttpRequest教程:“动态网页接口”:http://www.xml.com/pub/a/2005/02/09/xml-http-request.html
• JavaScript性能基准:http://blogs.ebusiness-apps.com/dave/?p=14
• AJAX资源:http://www.ajaxmatters.com
• JavaScript规范:http://www.ecma-international.org/publications/standards/Ecma-262.htm
• 介绍JavaScript对象标识:http://www.crockford.com/JSON/index.html
•  Mozilla 的Venkman JavaScript调试器:http://www.mozilla.org/projects/venkman/
•  XML DOM参考:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
xmlsdk/html/e9da2722-7879-4e48-869c-7f16714e2824.asp
• Microsoft Dynamic HTML reference: http://msdn.microsoft.com/library/default.asp?url=/
workshop/author/dhtml/reference/dhtml_reference_entry.asp
• Gecko DOM Reference: http://www.mozilla.org/docs/dom/domref/
• "“移植IE应用到Mozilla”
http://www-128.ibm.com/developerworks/web/library/wa-ie2mozgd/
• Mozilla XUL reference:
http://www.xulplanet.com/
• Microsoft XAML reference:
http://windowssdk.msdn.microsoft.com/library/default.asp?url=/library/
en-us/wcp_conceptual/html/0ff5f36e-dd84-44d1-aa3e-5bb4f147b169.asp?frame=true
• James Jesses Garret introduced the term AJAX in his article "AJAX: A New Approach to Web Applications," (Adaptive Path, February 2005): “AJAX:新的网页应用开发方式”
http://www.adaptivepath.com/publications/essays/archives/000385.php
• JetBrains IntelliJ IDEA:
http://www.jetbrains.com/
• Microsoft Visual Studio:
http://msdn.microsoft.com/vstudio/
• JSEditor:
http://jseditor.sourceforge.net/
• JSEclipse:
http://www.interaktonline.com/Products/Eclipse/JSEclipse/Overview/
• ActiveState Komodo:
http://www.activestate.com/Products/Komodo/
• XHTML:
http://www.w3.org/TR/xhtml1/
• Document Object Model:
http://www.w3.org/DOM/
• Cascading Style Sheets:
http://www.w3.org/Style/CSS/
• Extensible Stylesheet Language:
http://www.w3.org/Style/XSL/
• XForms:
http://www.w3.org/MarkUp/Forms/
• Scaling Vector Graphics:
http://www.w3.org/Graphics/SVG/
• XPath:
http://www.w3.org/TR/xpath
• AJAX.Net:
http://ajax.schwarz-interactive.de/csharpsample/default.aspx
• Backbase:
http://www.backbase.com
• Bitkraft:
http://www.tiggrbitz.com/
• Django:
http://www.djangoproject.com/
• Dojo:
http://www.dojotoolkit.org/
• DWR (Direct Web Reporting):
http://getahead.ltd.uk/dwr/
• MochiKit:
http://mochikit.com/
• Prototype:
http://prototype.conio.net/
• Rico:
http://openrico.org/rico/home.page
• Sajax:
http://www.modernmethod.com/sajax/
• Sarissa:
http://sarissa.sourceforge.net/doc/
• Script.aculo.us:
http://script.aculo.us/
• Ruby on Rails:
http://www.rubyonrails.org/
• For more on AJAX and DWR, read "AJAX Made Simple with DWR," Cloves Carneiro Jr. (JavaWorld, June 2005): 关于AJAX和DWR,请阅读“AJAX使用DWR更简单”
http://www.javaworld.com/javaworld/jw-06-2005/jw-0620-dwr.html
• For more articles on Java development tools, browse the Development Tools section of JavaWorld’s Topical Index: 更多Java开发工具的文章,请浏览JavaWorld的开发工具部分索引页
http://www.javaworld.com/channel_content/jw-tools-index.shtml
• For more articles on XML, browse the Java and XML section of JavaWorld’s Topical Index: 更多XML的文章,请浏览JavaWorld的Java和XML部分索引页
http://www.javaworld.com/channel_content/jw-xml-index.shtml
• For more articles on UI design, browse the User Interface Design section of JavaWorld’s Topical Index: 更多UI设计的文章,请浏览JavaWorld的UI设计部分索引页
http://www.javaworld.com/channel_content/jw-ui-index.shtml


原文地址:
posted @ 2006-01-04 10:53 只牵这只狗 阅读(203) | 评论 (0)编辑 收藏

2005年12月30日 #

2.3 业务逻辑层设计

  2.3.1 动态加载技术

  如果一次性获取完整的先序树,构造成xml提供给JavaScript解析,数据量越大,消耗的资源越多,客户端响应延迟时间就越长,因此对于大数据量的树,采用动态加载方式,即每次单击“+”图片时,判断是否已加载子节点数据,如果未加载则通过Ajax的XMLHTTP组件XMLHTTPRequest对象异步发送请求,连接服务器执行SQL 语句“select * from tree_class where parent = ?order by classcode ”获取节点数据。相关JavaScript 代码如下:

/*判断是否已经加载数据,未加载则访问服务器加载数据*/

dhtmlTree.prototype.Loading=function(pObject){
 if(((pObject.XMLload==0)&&(this.XMLsource))&&(!this.XMLloading)){
  pObject.XMLload=1;
  this.loadXML(this.XMLsource+getUrlSymbol(this.XMLsource)+"id="+escape(pObject.id));
 }
}
dtmlXMLObject.prototype.loadXML=function(url){//加载数据
 try {
  this.xmlDoc = new XMLHttpRequest();
  /*通过GET方法异步连接到 url 加载数据*/
  this.xmlDoc.open("GET", url,true);//true:异步;false:同步
  this.xmlDoc.send(null);
 } catch(e){
  this.xmlDoc = new ActiveXObject("Microsoft.XMLHTTP");//使用IE
  this.xmlDoc.open("GET", url,true);//true:异步;false:同步
  this.xmlDoc.send(null);
 }
 return this.xmlDoc.responseXML;
}

  每次只取同一个父节点ParentId的子节点序列,按XML格式封装成树的文档结构,例如:

<tree id="0">
<leaf child=”1" name="国防科技大学" id="1" im0="leaf.gif" im1="folderOpen.gif" im2=" folderClosed.gif"/>
</tree>

  提供给JavaScript的dhtmlTreeObject.prototype.insertItem()解析并组织好html输出节点;其中 child:1表示有子节点,0表示没有子节点;im0表示没有子节点时的图标;im1表示有子节点并且打开节点时的图标;im2表示有子节点并且关闭时的图标;所以还可以在构造XML时自定义图标。

  2.3.2 树型结构的构造

  从数据库中返回的是有序的先序树,而XML是完整的树型结构文档,所以将树型数据构造成预定义的XML格式,只需从根节点开始,遍历一遍树,即可将树全部生成。相关JavaScript代码如下:

/*动态加载树的构造方法*/

dtmlXMLObject.prototype.constructTree=function(){

//采用动态加载时获取的xml数据,解析树型数据

var node=this.XMLLoader.getXMLTopNode("tree");

var parentId=node.getAttribute("id");

for(var i=0;i<node.childNodes.length;i++) { //逐个解析xml文件的leaf节点

 if((node.childNodes[i].nodeType==1)&&(node.childNodes[i].tagName == "leaf")){
  var name=node.childNodes[i].getAttribute("text");
  …………
  var temp=dhtmlObject.a0Find(parentId);//获取父节点对象
  temp.XMLload=1;//已加载
  //构造html输出节点
  dhtmlObject.insertItem(parentId,cId,name,im0,im1,im2,chd);
  dhtmlObject.addDragger = this;//设置可拖放的对象
 };
}

  2.3.3 树型结构的维护

  在维护树型结构表时,删除节点较为简单,SQL 语句为: "delete from tree_class where classcode like′"+ classcode +"%′",即可将其节点和孩子一并删除;增加节点时,分为前插、后插、和插入子节点三种情况,前两种情况需要更新递归更新类别代码,后者只需找到父节点的孩子的最大类别代码加1 后,作为增加节点的类别代码;通过拖放来改变树的结构时,只需将拖动节点的parentId更新为目标节点的Classid即可,对应的SQL语句为:"update tree_class set parentId = "+ classidTo+" where classid = "+ classidFrom。

  3、效率分析

  对于树的存储一般有两种形式:二维表和链表,遍历方式一般也有深度遍历和广度遍历两种方式,遍历的时间复杂度都是O( n )。用二维表存储时,在内存中用数组的下标能准确定位节点的父节点、兄弟节点所在的数组下标。数据库中节点的定位也是准确的,但是将节点信息从数据库中读到内存中时,如果无法通过内存数组下标定位节点信息,那么就必须遍历一遍寻找一个节点,n 个节点中寻找一个节点的时间是O(n/2),n 个节点排序的时间复杂度将是O( n 2/2),这也是一般实现的B/S 模式的树结构效率低下的原因。本方案采用字典序编号方案,使得从数据库中取得的树是已经排序的,直接遍历生成客户页面程序,时间复杂度为O( n )。

  4、结 论

  本文讨论了基于Ajax的动态树型结构的实现方案,支持无刷新动态维护树的节点信息,支持拖放节点改变树的节点结构以及次序;同时采用数据库存储节点信息,保证了该方案有一定的通用性,此外结合XML描述树的节点信息,使得任何按本方案预定的xml文档描述的信息都可以通过树来展现。本方案已经应用在我校的数字迎新系统以及老百姓大药房信息系统中。

posted @ 2005-12-30 08:32 只牵这只狗 阅读(387) | 评论 (0)编辑 收藏

2.1 表示层实现

  类似Windows资源管理器的文件夹模式,节点的图片样式如表1所示。对于每个节点的DHTML 代码,需要包含节点的位置、前导图片、样式、针对该节点的其他操作等。同时为了节点显示的连贯性,还需一些前导图片。

  表1 树节点的前的图片样式表

 对于树的非叶子节点,图片和节点信息等,采用一个DIV ( division) 容器包含。DIV 等容器是DHTML 的基础,使用它可以通过脚本程序对其属性进行操作,如设置其style 样式的display 属性来控制子节点的展开和隐藏。节点的位置、前导图片、样式、针对该节点的其他的操作等都放入容器中,例:

< DIV id =mParentID>
< IMG align = center border = 0 onclick =″nodeExpand (‘leafid’)″ name = m1Tree src =′Tplus.gif′>
< IMG align = center border = 0 name = m1Folder src =′folderClosed. gif′> 计算机学院 </p>

  叶子节点无需容器直接输出即可。

  当点击某节点前的“ + ”、“ - ”图片时通过DIV 的style 样式的display 属性控制子节点的展开和隐藏。display:“none”(隐藏,不可见),display:“block”(显示) 。相关JavaScript 代码如下:

if (expandChild.style.display = =″none″){
 // 当前为隐藏状态,执行展开动作
 this.Loading(parentObject);//判断该分支的数据是否已经加载
 expandChild.style.display =″block″;
if (para2 = =″last″)
 parentObject.src =″Lminus. gif″; // 最后一个节点
else
 parentObject.src = ″Tminus. gif″; // 显示┠
 expandFolder.src = ″folderOpen. gif″;
}else {
 // 将当前节点的子节点全部隐藏
 expandChild.style.display = ″none″;
 if (para2 = = ″last″)
  parentObject.src = ″Lplus. gif″;
 else
  parentObject.src = ″Tplus. gif″;
  expandFolder.src = ″folderClosed. gif″;
}

  2.2 树型表结构设计

  我们以数据库为载体记录节点的变化,树型表结构至少要有以下字段:节点的编号(CLASSID) ,对节点的描述(ClassName),父节点的编号(ParentId),这些是构建树结构所必须的信息。同时引入节点的类别代码(ClassCode),节点的级别(ClassLevel),是否叶子节点 (Terminated)等辅助字段,记录节点次序,实体关系图如图3所示。


图 3 树型表结构示意图

  树遍历的时间复杂度是O( n ),但是将树信息存放到数据库后,就不能按传统的方式遍历树,必须使用SQL 语句访问数据库表的内容,而一次性取的数据量越多,消耗的资源也越多,用户等待的时间就越长。如果将无序的数据从数据库中读出,在服务器端,必须将排序后的树送到客户端显示。因此,最好从数据库读出已排好序的树。

  我们知道,字符串排序是按照字典序形式。结合SQL 语句的特点和树结构特点,数据库表中,节点的类别代码采用多级字符串形式,如AAABBBCCC,从树根节点开始,每向下一级字符串就增加一级,并且子节点类别代码以父节点类别代码开始,再开始本级的类别代码。同级的节点按照生成的顺序编号,如节点类别代码为AAA 的下一级孩子类别代码为AAAAAA,AAAAAB 等,AAAAAB 的孩子节点为AAAAABAAA、AAAAABAAB等。每一级编号字符的宽度与实际的应用关联,如AAA~ZZZ 一级则有263 个节点,如果不够用再增加一个字符用于编码。该巧妙的编号方式。使得在执行SQL 语句select * from tree_class order by classcode 后,一次获得完整的先序.

原文链接:http://www.7dspace.com/doc/44/0512/2005122906292220003.htm

posted @ 2005-12-30 08:31 只牵这只狗 阅读(391) | 评论 (0)编辑 收藏