ivaneeo's blog

自由的力量,自由的生活。

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  669 Posts :: 0 Stories :: 64 Comments :: 0 Trackbacks

范例(Examples)
Java 2拥有一组全新群集(collections)--并非仅仅加入一些新classes,而是完全改变了群集的风格.所以在Java 1.1Java 2中,封装群集的方式也完全不同.我首先讨论Java 2的方式,因为我认为功能更强大的Java 2 collections会取代Java 1.1 collections的地位.

范例(Examples): Java 2

假设有个人要去上课.我们用一个简单的Course来表示[课程]:
class Course...
   public Course(String name, boolean isAdvanced) {...};
   public boolean isAdvanced() {...};

我不关心课程其他细节.我感兴趣的是表示[人]的Person:
class Person...
   public Set getCourse() {
      return _courses;
   }
   public void setCourse(Set arg) {
      _courses = arg;
   }
   private Set _courses;

有了这个接口,我们就可以这样为某人添加课程:
   Person kent = new Person();
   Set s = new HashSet();
   s.add(new Course("Smalltalk Programming", false));
   s.add(new Course("Appreciating Single Malts", true));
   kent.setCourses(s);
   Assert.equals(2, Kent.getCourses().size());

   Course refact = new Course("Refactoring", true);
   kent.getCourses().add(refact);
   kent.getCourses().add(new Course("Brutal Sarcasm", false));
   Assert.equals(4, kent.getCourses().size());

   kent.getCourses().remove(refact);
   Assert.equals(3, kent.getCourses().size());
如果想了解高级课程,可以这么做:
   Iterator iter = person.getCourses().iterator();
   int count = 0;
   while(iter.hasNext()) {
      Course each = (Course)iter.next();
      if(each.isAdvanced()) count++;
   }
我要做的第一件事就是为Person中的群集(collections)建立合适的修改函数(modifiers, 亦即add/remove函数),如下所示,然后编译:
class Person...
   public void addCourse(Course arg) {
      _courses.add(arg);
   }
   public void removeCourse(Course arg) {
      _courses.remove(arg);
   }

如果我想下面这样初始化_courses值域,我的人生会轻松得多:
   private Set _courses = new HashSet();

接下来我需要观察设值函数(setter)的调用者.如果有许多地点大量运用了设值函数,我就需要修改设值函数,令它调用添加/移除(add/remove)函数.这个过程的复杂度取决于设值函数的被使用方式.设值函数的用法有两种,最简单的情况就是:它被用来[对集群进行初始化动作].换句话说,设值函数被调用之前,_courses是个空群集.这种情况下我需要修改设值函数,令它调用添加函数(add)就行了:
class Person...
   public void setCourses(Set arg) {
      Assert.isTrue(_courses.isEmpty());
      Iterator iter = arg.iterator();
      while(iter.hasNext()) {
         addCourse((Course)iter.next());
      }
   }
修改完毕后,最后以Rename Method(273)更明确地展示这个函数的意图.
   public void initializeCourses(Set arg) {
      Assert.isTrue(_courses.isEmpty());
      Iterator iter = arg.iterator();
      while(iter.hasNext()) {
         addCourse((Course)iter.next());
      }
   }

更普通的情况下,我必须首先以移除函数(remove)将群集中的所有元素全部移除,然后再调用添加函数(add)将元素一一添加进去.不过我发现这种情况很少出现(唔,愈是普通的情况,愈少出现).

如果我知道初始化时,除了添加元素,不会再有其他行为,那么我可以不使用循环,直接调用addAll()函数:
   public void initializeCourses(Set arg) {
      Assert.isTrue(_courses.isEmpty());
      
_courses.addAll(arg);
   }

我不能仅仅对这个set赋值,就算原本这个set是空的也不行.因为万一用户在[把set传递给Person对象]之后又去修改它,会破坏封装.我必须像上面那样创建set的一个拷贝.

如果用户仅仅只是创建一个set,然后使用设值函数(setter.译注:目前已改名为initializeCourses()),我可以让它们直接使用添加/移除(add/remove)函数,并将设值函数完全移除.于是,以下代码:
   Person kent = new Person();
   Set s = new HashSet();
   s.add(new Course("Smalltalk Programming", false));
   s.add(new Course("Appreciating Single Malts", true));
   kent.initializeCourses(s);

就变成了:
   Person kent = new Person();
   kent.addCourse(new Course("Smalltalk Programming", false));
   kent.addCourse(new Course("Appreciating Single Malts", true));



接下来我开始观察取值函数(getter)的使用情况.首先处理[有人以取值函数修改底部群集(underlying collection)]的情况,例如:
   kent.getCourses().add(new Course("Brutal Sarcasm", false));
这种情况下我必须加以改变,使它调用新的修改函数(modifier):
   kent.addCourse(new Course("Brutal Sarcasm", false));
修改完所有此类情况之后,我可以让取值函数(getter)返回一个只读映件(read-only view),用以确保没有任何一个用户能够通过取值函数(getter)修改群集:
   public Set getCourses() {
      return Collections.unmodifiableSet(_courses);
   }
这样我就完成了对群集的封装.此后,不通过Person提供的add/remove函数,谁也不能修改群集内的元素.

将行为移到这个class中

我拥有了合理的接口.现在开始观察取值函数(getter)的用户,从中找出应该属于Person的代码.下面这样的代码就应该搬移到Person去:
   Iterator iter = person.getCourses().iterator();
   int counter = 0;
   while(iter.hasNext()){
      Course each = (Course)iter.next();
      if(each.isAdvanced()) cout++;
   }

因为以上只使用了属于Person的数据.首先我使用Extract Method(110)将这段代码提炼为一个独立函数:
   int numberOfAdvancedCourses(Person person) {
      Iterator iter = person.getCourses().iterator();
      int count = 0;
      while(iter.hasNext()) {
         Course each = (Course)iter.next();
         if(each.isAdvanced()) count++;
      }
      return count;
   }
然后使用Move Method(142)将这个函数搬移到Person中:
class Person...
   int numberOfAdvancedCourses(Person person) {
      Iterator iter = person.getCourses().iterator();
      int count = 0;
      while(iter.hasNext()) {
         Course each = (Course)iter.next();
         if(each.isAdvanced()) count++;
      }
      return count;
   }

举个常见例子,下列代码:
   kent.getCourses().size();
可以修改更具可读性的样子,像这样:

kent.numberOfCourses();

class Person...
public int numberOfCourses() {
   return _courses.size();
}

数年以前,我曾经担心将这样的行为搬移到Person中会导致Person变得臃肿.但是在实际工作经验中,我发现这通常并不成为问题.

posted on 2005-09-19 13:51 ivaneeo 阅读(521) 评论(0)  编辑  收藏 所属分类: refactoring-从地狱中重生

只有注册用户登录后才能发表评论。


网站导航: