ivaneeo's blog

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

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            669 Posts :: 0 Stories :: 64 Comments :: 0 Trackbacks

          范例(Examples)
          Java 2擁有一組全新群集(collections)--并非僅僅加入一些新classes,而是完全改變了群集的風(fēng)格.所以在Java 1.1Java 2中,封裝群集的方式也完全不同.我首先討論Java 2的方式,因為我認(rèn)為功能更強大的Java 2 collections會取代Java 1.1 collections的地位.

          范例(Examples): Java 2

          假設(shè)有個人要去上課.我們用一個簡單的Course來表示[課程]:
          class Course...
             public Course(String name, boolean isAdvanced) {...};
             public boolean isAdvanced() {...};

          我不關(guān)心課程其他細(xì)節(jié).我感興趣的是表示[人]的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)建立合適的修改函數(shù)(modifiers, 亦即add/remove函數(shù)),如下所示,然后編譯:
          class Person...
             public void addCourse(Course arg) {
                _courses.add(arg);
             }
             public void removeCourse(Course arg) {
                _courses.remove(arg);
             }

          如果我想下面這樣初始化_courses值域,我的人生會輕松得多:
             private Set _courses = new HashSet();

          接下來我需要觀察設(shè)值函數(shù)(setter)的調(diào)用者.如果有許多地點大量運用了設(shè)值函數(shù),我就需要修改設(shè)值函數(shù),令它調(diào)用添加/移除(add/remove)函數(shù).這個過程的復(fù)雜度取決于設(shè)值函數(shù)的被使用方式.設(shè)值函數(shù)的用法有兩種,最簡單的情況就是:它被用來[對集群進(jìn)行初始化動作].換句話說,設(shè)值函數(shù)被調(diào)用之前,_courses是個空群集.這種情況下我需要修改設(shè)值函數(shù),令它調(diào)用添加函數(shù)(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)更明確地展示這個函數(shù)的意圖.
             public void initializeCourses(Set arg) {
                Assert.isTrue(_courses.isEmpty());
                Iterator iter = arg.iterator();
                while(iter.hasNext()) {
                   addCourse((Course)iter.next());
                }
             }

          更普通的情況下,我必須首先以移除函數(shù)(remove)將群集中的所有元素全部移除,然后再調(diào)用添加函數(shù)(add)將元素一一添加進(jìn)去.不過我發(fā)現(xiàn)這種情況很少出現(xiàn)(唔,愈是普通的情況,愈少出現(xiàn)).

          如果我知道初始化時,除了添加元素,不會再有其他行為,那么我可以不使用循環(huán),直接調(diào)用addAll()函數(shù):
             public void initializeCourses(Set arg) {
                Assert.isTrue(_courses.isEmpty());
                
          _courses.addAll(arg);
             }

          我不能僅僅對這個set賦值,就算原本這個set是空的也不行.因為萬一用戶在[把set傳遞給Person對象]之后又去修改它,會破壞封裝.我必須像上面那樣創(chuàng)建set的一個拷貝.

          如果用戶僅僅只是創(chuàng)建一個set,然后使用設(shè)值函數(shù)(setter.譯注:目前已改名為initializeCourses()),我可以讓它們直接使用添加/移除(add/remove)函數(shù),并將設(shè)值函數(shù)完全移除.于是,以下代碼:
             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));



          接下來我開始觀察取值函數(shù)(getter)的使用情況.首先處理[有人以取值函數(shù)修改底部群集(underlying collection)]的情況,例如:
             kent.getCourses().add(new Course("Brutal Sarcasm", false));
          這種情況下我必須加以改變,使它調(diào)用新的修改函數(shù)(modifier):
             kent.addCourse(new Course("Brutal Sarcasm", false));
          修改完所有此類情況之后,我可以讓取值函數(shù)(getter)返回一個只讀映件(read-only view),用以確保沒有任何一個用戶能夠通過取值函數(shù)(getter)修改群集:
             public Set getCourses() {
                return Collections.unmodifiableSet(_courses);
             }
          這樣我就完成了對群集的封裝.此后,不通過Person提供的add/remove函數(shù),誰也不能修改群集內(nèi)的元素.

          將行為移到這個class中

          我擁有了合理的接口.現(xiàn)在開始觀察取值函數(shù)(getter)的用戶,從中找出應(yīng)該屬于Person的代碼.下面這樣的代碼就應(yīng)該搬移到Person去:
             Iterator iter = person.getCourses().iterator();
             int counter = 0;
             while(iter.hasNext()){
                Course each = (Course)iter.next();
                if(each.isAdvanced()) cout++;
             }

          因為以上只使用了屬于Person的數(shù)據(jù).首先我使用Extract Method(110)將這段代碼提煉為一個獨立函數(shù):
             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)將這個函數(shù)搬移到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();
          }

          數(shù)年以前,我曾經(jīng)擔(dān)心將這樣的行為搬移到Person中會導(dǎo)致Person變得臃腫.但是在實際工作經(jīng)驗中,我發(fā)現(xiàn)這通常并不成為問題.

          posted on 2005-09-19 13:51 ivaneeo 閱讀(544) 評論(0)  編輯  收藏 所屬分類: refactoring-從地獄中重生
          主站蜘蛛池模板: 施秉县| 金川县| 福泉市| 宣恩县| 开封市| 潜山县| 安仁县| 左云县| 衡阳县| 波密县| 林口县| 阳谷县| 汝阳县| 巢湖市| 双桥区| 阳西县| 鄯善县| 鲁甸县| 西林县| 来安县| 西宁市| 六盘水市| 京山县| 霍州市| 开阳县| 安阳市| 九台市| 日喀则市| 长泰县| 富平县| 如皋市| 榆中县| 赣榆县| 项城市| 娱乐| 义乌市| 福鼎市| 分宜县| 莱阳市| 玉环县| 武义县|