讀《重構》,學習Collections.unmodifiableList()用法

yangxi_001發表於2014-01-14

在《重構——改善既有程式碼的設計》一書中,有一種重構手法叫Encapsulate Collection
(封裝叢集),為了演示該重構手法,我寫了四個類,通過對比重構前後的程式碼,加深對
這一重構手法的理解。

類Student有一ArrayList屬性,如果沒有閱讀《重構——改善既有程式碼的設計》一書,
很多人可能會像我一樣,如下設計類Student。但是,如果通過Student.getCourses()
獲得對ArrayList屬性引用後,就可以任意為Student物件新增“課程”,而Student物件
對此一無所知,這不符合物件導向程式設計的習慣。

package com.readonlylist;

import java.util.ArrayList;

public class Student
{
    private String name;

    private ArrayList<String> courses;

    public Student(String name, ArrayList<String> courses)
    {
this.name = name;
this.courses = courses;
    }
    
    public ArrayList<String> getCourses()
    {
        return courses;
    }

    public void setCourses(ArrayList<String> courses)
    {
        this.courses = courses;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }    
}

package com.readonlylist;

import java.util.ArrayList;

public class Test
{
    public static void main(String[] args)
    {
ArrayList<String> list = new ArrayList<String>();
list.add("001");
list.add("002");
        Student s = new Student("Tom", list);
        
        ArrayList<String> anotherList = s.getCourses();
        
        anotherList.add("999");
        
        System.out.println("Tom's course.length = " + s.getCourses().size());
    }
}

重構後的Student類如下所示:

package com.readonlylist;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Student1
{
    private String name;

    private ArrayList<String> courses;
    
    public Student1(String name, ArrayList<String> courses)
    {
this.name = name;
this.courses = courses;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }
    
    public void addCourse(String course)
    {
courses.add(course);
    }
    
    public String removeCourse(String course)
    {
boolean removed = courses.remove(courses);

if (removed)
{
     return course;
}
else
{
            return null;
}
    }
    
    public List<String> getCourses()
    {
return Collections.unmodifiableList(courses);
    }
}

package com.readonlylist;

import java.util.List;
import java.util.ArrayList;

public class Test1
{
    public static void main(String[] args)
    {
ArrayList<String> list = new ArrayList<String>();
list.add("001");
list.add("002");
        Student1 s = new Student1("Tom", list);
        
        List<String> anotherList = s.getCourses();
        
        /**
         * throws java.lang.UnsupportedOperationException
         * should replace with s.addCourse(String course)
         */
        anotherList.add("999"); 
        
        // never reached
        System.out.println("Tom's course.length = " + s.getCourses().size());        
    }
}

重構後,Student1類,僅對外提供的getCourses()方法,而沒有setCourses()方法,而且
通過getCourses()方法獲得的courses是“只讀的”,如果你試圖向其新增一個新課程,則
丟擲java.lang.UnsupportedOperationException。你必須通過Student1.addCourse()來
向特定的Student1物件新增一個新課程。就好像,你必須讓顧客自己向購物車裡放食物,
而不能在顧客毫不知情下,偷偷向其購物車裡放食物。

真是萬物皆通情理啊:)

相關文章