Encapsulate Collection (封装集合)

Summary:有个函数返回一个集合。让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数                                               java

Motivation: 咱们经常会在一个类中使用集合(collection,多是arraylistsetvector)来保存一组实例。这样的类一般也会提供针对该集合的取值/设值函数。数据结构

可是,集合的处理方式应该和其余种类的数据略有不一样。取值函数不应返回集合自身,由于这会让用户得以修改集合内容而集合拥有者却一无所悉。这也会对用户暴露过多对象内部数据结构的信息。若是一个取值函数确实须要返回多个值,它应该避免用户直接操做对象内所保存的集合,并隐藏对象内与用户无关的数据结构。至于如何作到这一点,视你使用的java版本不一样而有所不一样。另外,不该该为这整个结婚提供一个设置函数,但应该提供用觉得集合添加/移除元素的函数。这样,集合拥有者(对象)就能够控制集合元素的添加和移除。函数

若是你作到以上几点,集合就被很好的封装起来了,这即可以下降集合拥有者和用户之间的耦合度。测试

Mechanicsspa

1.加入为集合添加/移除元素的函数code

2.将保存集合的字段初始化为一个空集合。对象

3.编译。接口

4.找出集合设值函数的全部调用者。你能够修改那个设值函数,让它使用上述新创建的“添加/移除元素”函数;也能够直接修改调用端,改让它们调用上述新创建的“添加/删除元素”函数。ci

两种状况下须要用到集合设值函数:(1)集合为空时;(2)准备将原有集合替换为另外一个集合时rem

你或许会想运用Rename Method 为集合设值函数更名:从setXxx()改成initializeXxx()replaceXxx().

5.编译,测试

6.找出全部“经过取值函数得到集合并修改其内容”的函数。逐一修改这些函数,让它们改用添加/移除函数。每次修改后,编译并测试。

7.修改完上述全部“经过取值函数得到集合并修改其内容”的函数后,修改取值函数自身,使它返回该集合的一个只读副本。

8.编译,测试

9.找出取值函数的全部用户,从中找出应该存在于集合所属对象内的代码。运用Extract Method Move Method将这些代码移到宿主对象去

   范例1.

   假设有我的要去上课。咱们用一个简单的Course来表示"课程":

class Course...
    public Course (String name, boolean isAdvanced){...};
    public boolean isAdvanced(){...};

我不关系课程其余细节。我感兴趣的是表示“人”的Person:

class Person ...
    public Set getCourses(){
        return _courses;
    }
    public void setCourses(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("Appreciationg 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中的集合创建合适的修改函数(即添加/移除函数),以下所示,而后编译:

class Person
    public void addCourse(Courese arg){
        _courses.add(arg);
    }
    public void removeCourse(Course arg){
        _courses.remove(arg);
    }

若是像下面这样初始化_courses字段,会轻松不少:

private Set _courses = new HashSet();

接下来,咱们须要观察设值函数的调用者。若是有许多地点大量运用了设值函数,就须要修改设值函数,令它调用添加/移除函数。这个过程的复杂度取决于设值函数的被使用方式。设值函数的用法有两种,最简单的状况就是:它被用来初始化集合。换句话说,设值函数被调用以前,_courses是个空集合。这种状况下只需修改设值函数,令它调用添加函数就好了:

class Person...
    public void setCourses(Set arg){
        Assert.isTrue(_courses.isEmpty());
        Iterator iter = arg.iterator();
        while(iter.hasNext()){
            addCourse((Course)iter.next());
        }
    }

修改完毕后,最好以Rename Method 更明确地展现这个函数的意图:

public void initializeCourses(Set arg){
        Assert.isTrue(_courses.isEmpty());
        Iterator iter = arg.iterator();
        while(iter.hasNext()){
            addCourse((Course)iter.next());
        }
    }

更普通的状况下,咱们必须首先调用移除函数将集合中的全部元素所有移除,而后再调用添加函数将元素一一添加进去。不过这种状况不多出现。

若是知道初始化时,除了添加元素,再也不有其余行为,那么咱们能够不适用循环,直接调用addAll()函数:

public void initializeCourses(Set arg){
    Assert.isTrue(_courses.isEmpty());
    _courses.addAll(arg);
}

咱们不能直接把传入的set赋值给_coureses字段,就算本来这个字段是空的也不行。由于万一用户在把set传递给Person对象以后又去修改set中的元素,就会破坏封装。咱们必须像上面那样建立set的一个副本。

若是用户仅仅只是建立一个set,而后使用设值函数,咱们可让它们直接使用添加/移除函数,并将设值函数彻底移除。因而,如下代码

Person kent = new Person();
Set s = new HashSet();
s.add(new Course("Smalltalk Programming",false));
s.add(new Course("Appreciationg Single Malts",true));
kent.setCourses(s);

就变成了:

Person kent = new Person();
kent.addCourse(new Course("Smalltalk Programming",false));
kent.addCourse(new Course("Appreciationg Single Malts",true));

接下来,开始观察取值函数的使用状况。首先处理“经过取值函数修改集合元素的状况”,例如:

kent.getCourses().add(new Course("Brutal Sarcasm", false));

这种状况必须加以改变,使它调用新的修改函数:

kent.addCourse(new Course("Brutal Sarcasm", false));

修改完全部此类状况以后,可让取值函数返回一个只读副本,用以确保没有任何一个用户可以经过取值函数修改集合:

public Set getCourses(){
    return Collections.unmodifiableSet(_courses);
}

这样就完成了对集合的封装。此后,不经过Person提供的添加/移除函数,谁也不能修改集合内的元素。

将行为移到这个类中

  咱们拥有了合理的接口。如今开始观察取值函数的用户,从中找出应该属于Person的代码。下面这样的代码就应该搬移到Person去:

Iterator iter = person.getCourses().iterator();
int count = 0;
while (iter.hasNext()){
    Course each = (Course) iter.next();
    if(each.isAdvanced()){
        count ++;
    }
}

由于以上只使用了属于Person的数据。首先,使用Extract Method将这段代码提炼为一个独立函数:

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将这个函数搬移到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.getCourse().size();

能够将其修改为更具可读性的样子,像这样:

kent.numberOfCourses();
class Person ...
    public int numberOfCourses(){
        return _courses.size();
    }
相关文章
相关标签/搜索