??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲精品人成无码中文毛片,亚洲一级黄色大片,国产精品亚洲一区二区三区在线http://www.tkk7.com/ivanwan/archive/2005/09/19/13396.htmlivaneeoivaneeoMon, 19 Sep 2005 05:51:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/19/13396.htmlhttp://www.tkk7.com/ivanwan/comments/13396.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/19/13396.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/13396.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/13396.html范例QExamplesQ?BR>Java 2拥有一l全新群?collections)--q仅仅加入一些新classes,而是完全改变了群集的风格.所以在Java 1.1?STRONG>Java 2?装集的方式也完全不同.我首先讨?STRONG>Java 2的方?因ؓ我认为功能更强大?STRONG>Java 2 collections会取?STRONG>Java 1.1 collections的地?

范例QExamplesQ? Java 2

假设有个M?我们用一个简单的Course来表C[评]:
class Course...
   public Course(String name, boolean isAdvanced) {...};
   public boolean isAdvanced() {...};

我不兛_评其他l节.我感兴趣的是表示[人]?STRONG>Person:
class Person...
   public Set getCourse() {
      return _courses;
   }
   public void setCourse(Set arg) {
      _courses = arg;
   }
   private Set _courses;

有了q个接口,我们可以这样ؓ某hd评:
   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());
如果想了解高U课E?可以q么?
   Iterator iter = person.getCourses().iterator();
   int count = 0;
   while(iter.hasNext()) {
      Course each = (Course)iter.next();
      if(each.isAdvanced()) count++;
   }
我要做的W一件事是为Person中的集(collections)建立合适的修改函数(modifiers, 亦即add/remove函数),如下所C?然后~译:
class Person...
   public void addCourse(Course arg) {
      _courses.add(arg);
   }
   public void removeCourse(Course arg) {
      _courses.remove(arg);
   }

如果我想下面q样初始化_courses值域,我的人生会轻村־?
   private Set _courses = new HashSet();

接下来我需要观察设值函?setter)的调用?如果有许多地点大量运用了讑ր函?我就需要修改设值函?令它调用d/U除(add/remove)函数.q个q程的复杂度取决于设值函数的被用方?讑ր函数的用法有两U?最单的情况是:它被用来[寚w进行初始化动作].换句话说,讑ր函数被调用之前,_courses是个I群?q种情况下我需要修改设值函?令它调用d函数(add)p?
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)更明地展示q个函数的意?
   public void initializeCourses(Set arg) {
      Assert.isTrue(_courses.isEmpty());
      Iterator iter = arg.iterator();
      while(iter.hasNext()) {
         addCourse((Course)iter.next());
      }
   }

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

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

我不能仅仅对q个set赋?q原本q个set是空的也不行.因ؓ万一用户在[把set传递给Person对象]之后又去修改?会破坏封?我必d上面那样创徏set的一个拷?

如果用户仅仅只是创徏一个set,然后使用讑ր函?setter.译注:目前已改名ؓinitializeCourses()),我可以让它们直接使用d/U除(add/remove)函数,q将讑ր函数完全移?于是,以下代码:
   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)的用情?首先处理[有h以取值函C改底部群?underlying collection)]的情?例如:
   kent.getCourses().add(new Course("Brutal Sarcasm", false));
q种情况下我必须加以改变,使它调用新的修改函数(modifier):
   kent.addCourse(new Course("Brutal Sarcasm", false));
修改完所有此cL况之?我可以让取值函?getter)q回一个只L?read-only view),用以保没有M一个用戯够通过取值函?getter)修改集:
   public Set getCourses() {
      return Collections.unmodifiableSet(_courses);
   }
q样我就完成了对集的封?此后,不通过Person提供的add/remove函数,谁也不能修改集内的元素.

行为移到这个class?BR>
我拥有了合理的接?现在开始观察取值函?getter)的用?从中扑և应该属于Person的代?下面q样的代码就应该搬移?STRONG>Person?
   Iterator iter = person.getCourses().iterator();
   int counter = 0;
   while(iter.hasNext())?BR>      Course each = (Course)iter.next();
      if(each.isAdvanced()) cout++;
   }

因ؓ以上只用了属于Person的数?首先我?STRONG>Extract Method(110)这D代码提gؓ一个独立函?
   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)这个函数搬UdPerson?
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();
}

数年以前,我曾l担心将q样的行为搬UdPerson中会DPerson变得臃肿.但是在实际工作经验中,我发现这通常q不成ؓ问题.



ivaneeo 2005-09-19 13:51 发表评论
]]>
重构?-重新l织数据QEncapsulate CollectionQ(3Q?-作法http://www.tkk7.com/ivanwan/archive/2005/09/15/13102.htmlivaneeoivaneeoThu, 15 Sep 2005 09:51:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/15/13102.htmlhttp://www.tkk7.com/ivanwan/comments/13102.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/15/13102.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/13102.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/13102.html
  • 作法QMechanics)
    • 加入[为群集添?add),U除(remove)元素]的函?
    • [用以保存集]的值域初始化ؓ一个空集.
    • ~译.
    • 扑և[集讑ր函数]的所有调用?你可以修攚w个设值函?让它使用上述新徏立的[d/U除元素]函数;也可以直接修改调用端,改让它们调用上述新徏立的[d/U除元素]函数.
        • ==>两种情况下需要用到[集讑ր函数];(1)集为空?(2)准备原有群集替换ؓ另一个群集时.
        • ==>你或怼惌?STRONG>Rename Method(273)为[集讑ր函数]改名,从setXxx()改ؓinitializeXxx()或replaceXxx().
    • ~译,试.
    • 扑և所有[通过取值函?getter)获得集q修改其内容]的函?逐一修改q些函数,让它们改用[d/U除](add/remove)函数.每次修改?~译q测?
    • 修改完上q所有[通过取值函?getter)获得集q修改群集内容]的函数后,修改取值函数自w?使它q回该群集的一个只L?read-only view).
        • ==>在Java 2?你可以用Collection.unmodifiableXxx()得到该集的只读映g.
        • ==>在Java 1.1?你应该返回群集的一份拷?
    • ~译,试.
    • 扑և取值函?getter)的所有用?从中扑և应该存在于[集之宿d?host object)]内的代码.q用Extract Method(110)?STRONG>Move Method(142)这些代码移到宿d象去.
  • 如果你用Java 2,那么本项重构到此为止.如果你用Java 1.1,那么用户也许会喜Ƣ用枚?enumeration).Z提供q个枚D,你应该这样做.
    • 修改现有取值函?getter)的名?然后d一个新取值函?使其q回一个枚?扑և旧取值函数的所有被使用?它们都改ؓ使用新取值函?
        • ==>如果q一步跨度太?你可以先使用Rename Method(273)修改原取值函数的名称;再徏立一个新取值函数用以返回枚?最后再修改所有调用?使其调用新取值函?
    • ~译,试.


    ivaneeo 2005-09-15 17:51 发表评论
    ]]>
    重构?-重新l织数据QEncapsulate CollectionQ(2Q?-动机http://www.tkk7.com/ivanwan/archive/2005/09/15/13085.htmlivaneeoivaneeoThu, 15 Sep 2005 07:35:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/15/13085.htmlhttp://www.tkk7.com/ivanwan/comments/13085.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/15/13085.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/13085.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/13085.html动机QMotivationQ?BR>class常常会用群?collection,可能是array,list,set或vector)来保存一l实?q样的class通常也会提供针对该群集[取?讑ր函数](getter/setter).

    但是,集群的处理方式应该和其他U类的数据略有不?取值函?getter)不该q回集自n,因ؓq将让用户得以修改群集内容而群集拥有者却一无所?q也会对用户暴露q多[对象内部数据l构]的信?如果一个取值函?getter)实需要返回多个?它应该避免用L接操作对象内所保存的群?q藏对象内[与用h关]的数据结?至于如何做到q一?视你使用的Java版本不同而有所不同.

    另外,不应该ؓq整个群集提供一个设值函?setter),但应该提供用以ؓ集d/U除(add/remove)元素的函?q样,集拥有?对象)可以控制群集元素的d和移?

    如果你做C上数?集(collection)p很好地封装v来了,q便可以降低集拥有?class)和用户之间的耦合?

    ivaneeo 2005-09-15 15:35 发表评论
    ]]>
    重构?-重新l织数据QEncapsulate CollectionQ(1Q?/title><link>http://www.tkk7.com/ivanwan/archive/2005/09/15/13082.html</link><dc:creator>ivaneeo</dc:creator><author>ivaneeo</author><pubDate>Thu, 15 Sep 2005 07:08:00 GMT</pubDate><guid>http://www.tkk7.com/ivanwan/archive/2005/09/15/13082.html</guid><wfw:comment>http://www.tkk7.com/ivanwan/comments/13082.html</wfw:comment><comments>http://www.tkk7.com/ivanwan/archive/2005/09/15/13082.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/ivanwan/comments/commentRss/13082.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/ivanwan/services/trackbacks/13082.html</trackback:ping><description><![CDATA[有个函数(<STRONG>method</STRONG>)q回一个群?<STRONG>collection</STRONG>).<BR><BR><STRONG>让这个函数返回该集的一个只L?read-only view),q在q个class中提供[d/U除](add/remove)集元素的函?</STRONG><BR><BR><IMG height=113 alt="Encapsulate Collection.jpg" src="http://www.tkk7.com/images/blogjava_net/ivanwan/pictures/Encapsulate%20Collection.jpg" width=421 border=0><img src ="http://www.tkk7.com/ivanwan/aggbug/13082.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/ivanwan/" target="_blank">ivaneeo</a> 2005-09-15 15:08 <a href="http://www.tkk7.com/ivanwan/archive/2005/09/15/13082.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>重构?-重新l织数据QEncapsulate FieldQ(3Q?-作法http://www.tkk7.com/ivanwan/archive/2005/09/15/13074.htmlivaneeoivaneeoThu, 15 Sep 2005 06:30:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/15/13074.htmlhttp://www.tkk7.com/ivanwan/comments/13074.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/15/13074.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/13074.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/13074.html
  • 作法QMechanics)
    • 为public值域提供取?讑ր函?getter/setter).
    • 扑ֈq个class以外使用该值域的所有地?如果客户只是使用该值域,把引用动作(reference)替换为对取值函?getter)的调用];如果客户修改了该值值域,将此一引用Ҏ换ؓ[对设值函?setter)的调用].
        • ==>如果q个值域是个对象,而客户只不过是调用该对象的某个函?那么不论该函数是否ؓ修改函数(modifier,会改变对象状?,都只能算是用该值域.只有当客户ؓ该值域赋值时,才能其替换值函?setter).
    • 每次修改之后,~译q测?
    • 值域的所有用户修改完毕后,把值域声明为private.
    • ~译,试.


    ivaneeo 2005-09-15 14:30 发表评论
    ]]>
    重构?-重新l织数据QEncapsulate FieldQ(2Q?-动机http://www.tkk7.com/ivanwan/archive/2005/09/15/13073.htmlivaneeoivaneeoThu, 15 Sep 2005 06:21:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/15/13073.htmlhttp://www.tkk7.com/ivanwan/comments/13073.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/15/13073.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/13073.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/13073.html动机QMotivationQ?BR>面向对象的首要原则之一是装(encapsulation),或者称为[数据隐藏](data hidding).

    public数据被看做是一U不好的作法,因ؓq样会降低程序的模块化程?modularity).如果数据和用该数据的行集中在一?一旦情况发生变?代码的修改就会比较简?因ؓ需要修改的代码都集中于同一块地?而不是星|棋布地散落在整个程序中.

    Encapsulate Field(206)是封装过E的W一?通过q项重构手法,你可以将数据隐藏h,q提供相应的讉K函数(accessors).但它毕竟只是W一?如果一个class除了讉K函数(accessors)外不能提供其他行?它终I只是一?STRONG>dumb class(哑类).q样的classq不能获得对象技术的优势,而你知道,费如何一个对象都是很不好?实施Encapsulate Field(206)之后,我会试L那些使用[新徏讉K函数]的函?看看是否可以通过单的Move Method(142)d地将它们Ud新对象去.

    ivaneeo 2005-09-15 14:21 发表评论
    ]]>
    重构?-重新l织数据QEncapsulate FieldQ(1Q?/title><link>http://www.tkk7.com/ivanwan/archive/2005/09/15/13069.html</link><dc:creator>ivaneeo</dc:creator><author>ivaneeo</author><pubDate>Thu, 15 Sep 2005 06:07:00 GMT</pubDate><guid>http://www.tkk7.com/ivanwan/archive/2005/09/15/13069.html</guid><wfw:comment>http://www.tkk7.com/ivanwan/comments/13069.html</wfw:comment><comments>http://www.tkk7.com/ivanwan/archive/2005/09/15/13069.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/ivanwan/comments/commentRss/13069.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/ivanwan/services/trackbacks/13069.html</trackback:ping><description><![CDATA[你的class中存在一个public值域.<BR><BR><STRONG>它声明为private,q提供相应的讉K函数(accessors).<BR></STRONG><BR><FONT style="BACKGROUND-COLOR: #d3d3d3">public String _name</FONT><BR><BR>                                       <IMG height=41 alt=126.gif src="http://www.tkk7.com/images/blogjava_net/ivanwan/pictures/126.gif" width=45 border=0><BR><FONT style="BACKGROUND-COLOR: #d3d3d3">private String _name;<BR>public String getName() {return _name;}<BR>public void setName(String arg) {_name = arg;}</FONT><img src ="http://www.tkk7.com/ivanwan/aggbug/13069.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/ivanwan/" target="_blank">ivaneeo</a> 2005-09-15 14:07 <a href="http://www.tkk7.com/ivanwan/archive/2005/09/15/13069.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>重构?-重新l织数据QReplace Magic Number with Symbolic ConstantQ(3Q?-作法http://www.tkk7.com/ivanwan/archive/2005/09/15/13068.htmlivaneeoivaneeoThu, 15 Sep 2005 06:01:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/15/13068.htmlhttp://www.tkk7.com/ivanwan/comments/13068.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/15/13068.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/13068.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/13068.html
  • 作法QMechanics)
    • 声明一个常?令其gؓ原本的魔法数?
    • 扑ևq个法数的所有引用点.
    • 查是否可以用这个新声明的常量来替换该魔法数.如果可以,便以一帔R替换?
    • ~译.
    • 所有魔法数都被替换完毕?~译q测?此时整个E序应该q{如常,像没有做Q何修改一?
        • ==>有个不错的测试办?查现在的E序是否可以被你LC改常量?q可能意x些预期结果将有所改变,以配合这一新?实际工作中ƈ非L可以q行q样的测?.如果可行,q就是一个不错的手法.


    ivaneeo 2005-09-15 14:01 发表评论
    ]]>
    重构?-重新l织数据QReplace Magic Number with Symbolic ConstantQ(2Q?-动机http://www.tkk7.com/ivanwan/archive/2005/09/15/13065.htmlivaneeoivaneeoThu, 15 Sep 2005 05:54:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/15/13065.htmlhttp://www.tkk7.com/ivanwan/comments/13065.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/15/13065.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/13065.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/13065.html动机QMotivationQ?BR>在计科学中,法?magic number)是历史最悠久的不良现象之一.

    q行本项重构之前,你应该先L其他替换Ҏ.你应该观察魔法数如何被?而后往往你会发现一U更好的使用方式.如果q个法数是?STRONG>type code(型别?,误虑使用Replace Type Code with Class(218);如果q个法C表一个数l的长度,请在遍历该数l的时?改用Array.length().

    ivaneeo 2005-09-15 13:54 发表评论
    ]]>
    重构?-重新l织数据QReplace Magic Number with Symbolic ConstantQ(1Q?/title><link>http://www.tkk7.com/ivanwan/archive/2005/09/15/13063.html</link><dc:creator>ivaneeo</dc:creator><author>ivaneeo</author><pubDate>Thu, 15 Sep 2005 05:46:00 GMT</pubDate><guid>http://www.tkk7.com/ivanwan/archive/2005/09/15/13063.html</guid><wfw:comment>http://www.tkk7.com/ivanwan/comments/13063.html</wfw:comment><comments>http://www.tkk7.com/ivanwan/archive/2005/09/15/13063.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/ivanwan/comments/commentRss/13063.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/ivanwan/services/trackbacks/13063.html</trackback:ping><description><![CDATA[你有一个字面数?literal number),带有Ҏ含义.<BR><BR><STRONG>创造一个常?Ҏ其意义ؓ它命?q将上述的字面数值替换ؓq个帔R.<BR></STRONG><BR><FONT style="BACKGROUND-COLOR: #d3d3d3">double potentialEnergy(double mass, double height) {<BR>   return mass * 9.81 * height;<BR>}<BR><BR><FONT style="BACKGROUND-COLOR: #ffffff">                                      <IMG height=41 alt=126.gif src="http://www.tkk7.com/images/blogjava_net/ivanwan/pictures/126.gif" width=45 border=0></FONT><BR>double potentialEnergy(double mass, double height) {<BR>   return mass * GRAVITATIONAL_CONSTANT * height;<BR>}<BR>static final double GRAVITATIONAL_CONSTANT = 9.81; </FONT><img src ="http://www.tkk7.com/ivanwan/aggbug/13063.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/ivanwan/" target="_blank">ivaneeo</a> 2005-09-15 13:46 <a href="http://www.tkk7.com/ivanwan/archive/2005/09/15/13063.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>重构?-重新l织数据QChange Bidirectional Association to UnidirectionalQ(4Q?-范例http://www.tkk7.com/ivanwan/archive/2005/09/14/13028.htmlivaneeoivaneeoWed, 14 Sep 2005 09:29:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/14/13028.htmlhttp://www.tkk7.com/ivanwan/comments/13028.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/14/13028.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/13028.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/13028.html范例QExamplesQ?BR>本例?STRONG>Change Unidirectional association to Bidirectional(197)留下的代码开始进?其中Customer?STRONG>Order之间有双向关?
    class Order...
       Customer getCustomer() {
          return _customer;
       }
       void setCustomer(Custoemr arg) ...

           if(_customer != null) _customer.friendOrders().remove(this);
           _customer = arg;
           if(_customer != null) _customer.friendOrders().add(this);
        }
       private Customer _customer;   //译注:q是Order-to-Customer link也是本例的移除对?

    class Customer ...
        void addOrder(Order arg) {
           arg.setCustomer(this);
        }
       private Set _orders = new HashSet();
       //译注:以上是Customer-to-Order link
       Set friendOrders() {
           return _orders;
        }


    后来我发?除非先有Customer对象,否则不会存在Order对象.因此我想[从Order到Customer的连接]U除?

    对于本项重构来说,最困难的就是检查可行?如果我知道本w构是安全?那么重构手法自n十分?问题在于是否有Q何代码倚赖_customer值域的存?如果实?那么在删除这个值域之后,我必L供替代品.

    首先,我需要研I所有读取这个值域的函?以及所有用这些函数的函数.我能扑ֈ另一条途径来供应Customer对象?---q通常意味Customer对象作ؓ引数(argument)传递给其用?某函?.下面是一个简化例?
    class Order...
       double getDiscountedPrice() {
          return getGrossPrice() * (1 - _customer.getDiscount());
       }

    改变?
    class Order...
       double getDiscountedPrice(Customer customer) {
          return getGrossPrice() * (1 - customer.getDiscount());
       }

    如果待改函数是被Customer对象调用?那么q样的修Ҏ案特别容易实?因ؓCustomer对象自׃为引?argument)传给函数很是Ҏ.所以下列代?
    class Customer...
       double getPriceFor(Order order) {
          Assert.isTrue(_orders.contains(order));   //see
    Introduce Assertion(267)
          return order.getDiscountedPrice();

    变成?
    class Customer...
       double getPriceFor(Order order) {
          Assert.isTrue(_orders.contains(order));
          return order.getDiscountedPrice(this);
    另一个作法就是修改取值函?getter),使其在不使用_customer值域的前提下q回一?STRONG>Customer对象.如果q行得?我就可以使用Substitute Algorithm(139)修改Order.getCustomer()函数法.我有可能q样修改代码:
    Customer getCustomer() {
       Iterator iter = Customer.getInstance().iterator();
       while(iter.hasNext()) {
          Customer each = (Customer)iter.next();
          if(each.containsOrder(this) return each;
       }
       return null;
    }

    q段代码比较?不过实可行.而且,在数据库环境?如果我需要用数据库查询语句,q段代码对系l性能的媄响可能ƈ不显?如果,Order class中有些函C用_customer值域,我可以实?STRONG>Self Encapsulate Field(171)令它们{而改用上q的getCustomer()函数.

    如果我要保留上述的取值函?getter),那么Order?STRONG>Customer的关联从接口上看虽然仍然是双?但实C已经是单向关pM.虽然我移除了反向指针,但两个classes彼此之间的依存关p?inter-dependencies)仍然存在.

    如果我要替换取值函?getter),那么我就专注地替换它,其他部分留待以后处理.我会逐一修改取值函数的调用?让它们通过其他来源取得Customer对象.每次修改后都~译q测?实际工作中这一q程往往相当?如果q个q程让我觉得很棘手很复杂,我会攑ּ本项重构.

    一旦我消除了_customer值域的所有读取点,我就可以着手处理[Ҏ值域q行赋值动作]的函C.很简?只要把这些赋值动作全部移?再把值域一q删?p?׃已经没有M代码需要这个值域,所以删掉它q不会带来Q何媄?

    ivaneeo 2005-09-14 17:29 发表评论
    ]]>
    重构?-重新l织数据QChange Bidirectional Association to UnidirectionalQ(3Q?-作法http://www.tkk7.com/ivanwan/archive/2005/09/14/13021.htmlivaneeoivaneeoWed, 14 Sep 2005 07:11:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/14/13021.htmlhttp://www.tkk7.com/ivanwan/comments/13021.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/14/13021.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/13021.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/13021.html
  • 作法QMechanics)
    • 扑և[你想去除的指针]的保存值域,查它的每一个用?判断是否可以去除该指?
        • ==>不但要检查[直接d点],也要查[直接d点]的调用函?
        • ==>考虑有无可能不通过指针函数取得[被引用对象](referred object).如果有可?你就可以对取值函?getter)使用Substitute Algorithm(139).从而让客户在没有指针的情况下也可以使用该取值函?
        • ==>对于使用该值域的所有函?考虑[被引用对象](referred object)作ؓ引数(argument)传进?
    • 如果客户使用了取值函?getter),先运用Self Encapsulate Field(171)[待除值域]自我装h,然后使用Subsitute Algorithm(139)对付取值函?令它不再使用?待除)值域.然后~译,试.
    • 如果客户q用取值函?getter),那就直接修改[待除值域]的所有被引用?改以其他途径获得该值域所保存的对?每次修改?~译q测?
    • 如果已经没有M函数使用?待除)值域,U除所有[对该值域的更新逻辑],然后U除该值域.
        • ==>如果有许多地方对此值域赋?先运?STRONG>Self Encapsulate Field(171)使这些地Ҏ用同一个设值函?setter).~译,试.而后这个设值函数的本体清空.再编?再测?如果q些都可?可以将此值域和其讑ր函?q同对设值函数的所有调?全部U除.
    • ~译,试.


    ivaneeo 2005-09-14 15:11 发表评论
    ]]>
    重构?-重新l织数据QChange Bidirectional Association to UnidirectionalQ(2Q?-动机http://www.tkk7.com/ivanwan/archive/2005/09/14/12998.htmlivaneeoivaneeoWed, 14 Sep 2005 03:20:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/14/12998.htmlhttp://www.tkk7.com/ivanwan/comments/12998.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/14/12998.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/12998.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/12998.html动机QMotivationQ?BR>双向兌(bidirectional associations)很有?但你也必Mؓ它付Z?那就是[l护双向链接,保对象被正创建和删除]而增加的复杂?而且,׃很多E序员ƈ不习惯用双向关?它往往成ؓ错误之源.

    大量的双向连?two-way links)也很Ҏ引发[僵尸对象]:某个对象本来已经该死亡了,却仍然保留在pȝ?因ؓ对它的各引用还没有完全清除.

    此外,双向兌也迫使两个classes之间有了怾?对其中Q一个class的Q何修?都可能引发另一个class的变?如果q两个classes处在不同的package?q种怾性就是packages之间的相?q多的依存?inter-dependencies)会造成q耦合(highly coupled)pȝ,使得M一点小改动都可能造成许多无法预知的后?

    只有在你需要双向关联的时?才应该用它.如果你发现双向关联不再有存在价?应该去掉其中不必要的一条关?

    ivaneeo 2005-09-14 11:20 发表评论
    ]]>
    重构?-重新l织数据QChange Bidirectional Association to UnidirectionalQ(1Q?/title><link>http://www.tkk7.com/ivanwan/archive/2005/09/14/12992.html</link><dc:creator>ivaneeo</dc:creator><author>ivaneeo</author><pubDate>Wed, 14 Sep 2005 03:08:00 GMT</pubDate><guid>http://www.tkk7.com/ivanwan/archive/2005/09/14/12992.html</guid><wfw:comment>http://www.tkk7.com/ivanwan/comments/12992.html</wfw:comment><comments>http://www.tkk7.com/ivanwan/archive/2005/09/14/12992.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/ivanwan/comments/commentRss/12992.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/ivanwan/services/trackbacks/12992.html</trackback:ping><description><![CDATA[两个classes之间有双向关?但其中一个class如今不再需要另一个class的特?<BR><BR><STRONG>去除不必要的兌(association).<BR><BR><IMG height=251 alt="Change Bidirectional Association to Unidirectional.jpg" src="http://www.tkk7.com/images/blogjava_net/ivanwan/pictures/Change%20Bidirectional%20%20Association%20to%20Unidirectional.jpg" width=401 border=0></STRONG><img src ="http://www.tkk7.com/ivanwan/aggbug/12992.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/ivanwan/" target="_blank">ivaneeo</a> 2005-09-14 11:08 <a href="http://www.tkk7.com/ivanwan/archive/2005/09/14/12992.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>重构?-重新l织数据QChange Unidirectional Association to BidirectionalQ(4Q?-范例http://www.tkk7.com/ivanwan/archive/2005/09/13/12891.htmlivaneeoivaneeoTue, 13 Sep 2005 07:55:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/13/12891.htmlhttp://www.tkk7.com/ivanwan/comments/12891.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/13/12891.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/12891.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/12891.html范例QExamplesQ?br> 下面是一D늮单程序,其中有两个classesQ表C[定单]?span style="font-weight: bold;">Order和表C[客户]?span style="font-weight: bold;">Customer?span style="font-weight: bold;">Order引用?span style="font-weight: bold;">CustomerQ?span style="font-weight: bold;">Customer则ƈ没有引用OrderQ?br> class Order...
        Customer getCustomer() {
           return _customer;
        }
        void setCustomer(Customer arg) {
           _Customer = arg;
        }
        Customer _customer;   //q是一个“Order”to “Customer”的q接

    首先Q我要ؓCustomerd一个值域。由于一个客户可以拥有多份定单,所以这个新增值域应该是个集QcollectionQ。我不希望同一份定单在同一个群集中出现一ơ以上,所以这里适合使用setQ?br style="background-color: rgb(211, 211, 211);"> class Customer {
        private Set _orders = new HashSet();
    现在Q我需要决定由哪一个class负责控制兌性(associationQ。我比较喜欢让单一class来操控,因ؓq样我就可以所有[兌处理逻辑]集中安置于一地。我按照下列步骤做一军_Q?br>
    1. 如果两者都?span style="font-weight: bold;">reference objectsQ而其间的兌是[一对多]关系Q那么就由[拥有单一reference]的那一Ҏ担[控制者]角色。以本例而言Q如果一个客户可拥有多䆾定单Q那么就?span style="font-weight: bold;">Order class(定单)来控制关联性?/li>
    2. 如果某个对象是另一个对象的l成QcomponentQ,那么由后者负责控制关联性?/li>
    3. 如果两者都?span style="font-weight: bold;">reference objectsQ而其间的兌是[多对多]关系Q那么随便其中哪个对象来控制兌性,都无所谓?/li>
    本例之中׃Order负责控制兌性,所以我必须为Customerd一个赋值函敎ͼ让Order可以直接讉K_ordersQ订单)集群?span style="background-color: rgb(211, 211, 211);">Order的修改函敎ͼmodifierQ将使用q个辅助函数Ҏ针两端对象进行同步控制。我这个赋值函数命名ؓfriendOrdersQ)Q表C个函数只能在q种Ҏ情况下用。此外,如果Order?span style="font-weight: bold;">Customer位在同一?span style="font-weight: bold;">package内,我还会将friendOrdersQ)声明为[package  可见度]Q其可见程度降x低?br>
    但如果这两个classes不在同一个package内,我就只好把friendOrdersQ)声明?span style="font-weight: bold;">public了?br> class Customer...
        Set friendOrders() {
           return _orders;
        }
    现在。我要改变修改函敎ͼmodifierQ,令它同时更新反向指针Q?br>
    class Order...
        void setCustomer(Custoemr arg) ...
           if(_customer != null) _customer.friendOrders().remove(this);
           _customer = arg;
           if(_customer != null) _customer.friendOrders().add(this);
        }

    classes之间的关联性是各式各样的,因此修改函数QmodifierQ的代码也会随之有所差异。如果_customer的g可能是nullQ我? 以拿掉上q的W一个null查,但仍焉要检查引敎ͼargumentQ是否是null。不q,基本形式L相同的:先让Ҏ删除[指向你]的指针,? 你的指针指向一个新对象Q最后让那个新对象把它的指针指向你?br>
    如果你希望在Customer中也能修改连接(linkQ,p它调用控制函敎ͼ

    class Customer ...
        void addOrder(Order arg) {
           arg.setCustomer(this);
        }
    如果一份订单也可以对应多个客户Q那么你所面的就是一个[多对多]情况Q重构后的函数可能是下面q样Q?br> class Order ...   //controlling methods
        void addCustomer(Customer arg) {
           arg.friendOrders().add(this);
           _customers.add(arg);
        }
        void removeCustomer(Customer arg) {
           arg.friendOrders().remove(this);
           _customers.remove(arg);
        }
    class Customer ...
        void addOrder(Order arg) {
           arg.addCustomer(this);
        }
        void removeOrder(Order arg) {
           arg.removeCustomer(this);
        }


    ivaneeo 2005-09-13 15:55 发表评论
    ]]>
    重构?-重新l织数据QChange Unidirectional Association to BidirectionalQ(3Q?-作法http://www.tkk7.com/ivanwan/archive/2005/09/13/12881.htmlivaneeoivaneeoTue, 13 Sep 2005 07:00:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/13/12881.htmlhttp://www.tkk7.com/ivanwan/comments/12881.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/13/12881.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/12881.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/12881.html
  • 作法QMechanics)
             
      • 在referred class中增加一个值域,用以保存[反向指针].
      • 军_由哪个class(引用端或被引用端)控制兌?association).
      • 在[被控制]建立一个辅助函?其命名应该清楚指出它的有限用?
      • 如果既有的修改函?modifier)在[控制端],让它负责更新反向指针.
      • 如果既有的修改函?modifier)在[控制端]


    ivaneeo 2005-09-13 15:00 发表评论
    ]]>
    重构?-重新l织数据(Change Unidirectional Association to Bidirectional)Q?Q?-动机http://www.tkk7.com/ivanwan/archive/2005/09/13/12845.htmlivaneeoivaneeoTue, 13 Sep 2005 02:31:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/13/12845.htmlhttp://www.tkk7.com/ivanwan/comments/12845.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/13/12845.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/12845.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/12845.html
    [反向指针]手法有点手,所以在你能够自在运用它之前,应该有相应的试.通常我不花心思去试讉K函数(accessors),因ؓ普通访问函数的风险没有高到需要测试的地步,但本重构要求试讉K函数,所以它是极数需要添加测试的重构手法之一.

    本重构运用反向指?back pointer)实现双向兌(bidirectionality).其他技?例如q接对象,link objects)需要其他重构手?

    ivaneeo 2005-09-13 10:31 发表评论
    ]]>
    重构?-重新l织数据QChange Unidirectional Association to BidirectionalQ(1Q?/title><link>http://www.tkk7.com/ivanwan/archive/2005/09/12/12746.html</link><dc:creator>ivaneeo</dc:creator><author>ivaneeo</author><pubDate>Mon, 12 Sep 2005 05:53:00 GMT</pubDate><guid>http://www.tkk7.com/ivanwan/archive/2005/09/12/12746.html</guid><wfw:comment>http://www.tkk7.com/ivanwan/comments/12746.html</wfw:comment><comments>http://www.tkk7.com/ivanwan/archive/2005/09/12/12746.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/ivanwan/comments/commentRss/12746.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/ivanwan/services/trackbacks/12746.html</trackback:ping><description><![CDATA[两个classes都需要用对方特性,但其间只有一条单向连接(one-way linkQ?br> <br> <span style="font-weight: bold;">d一个反向,q修改函数QmodifiersQ能够同时更C条连?/span>。(注译Q这里的指针{同于句柄(handleQ,修改函数QmodifierQ指的是改变双方关系者)<br> <br> <img src="http://www.tkk7.com/images/blogjava_net/ivanwan/pictures/Change%20Unidirectional%20Association%20to%20Bidirectional.png" alt="Change Unidirectional Association to Bidirectional.png" border="0" height="271" width="403"><br> <img src ="http://www.tkk7.com/ivanwan/aggbug/12746.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/ivanwan/" target="_blank">ivaneeo</a> 2005-09-12 13:53 <a href="http://www.tkk7.com/ivanwan/archive/2005/09/12/12746.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>重构?-重新l织数据QDuplicate Observed DataQ(3Q?-作法http://www.tkk7.com/ivanwan/archive/2005/09/07/12308.htmlivaneeoivaneeoWed, 07 Sep 2005 04:19:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/07/12308.htmlhttp://www.tkk7.com/ivanwan/comments/12308.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/07/12308.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/12308.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/12308.html作法QMechanics)
      • 修改presentation classQ其成?span style="font-weight: bold;">domain class?span style="font-weight: bold;">Observer[GoF]?/li>
          • ==》如果尚未有domain classQ就建立一个?/li>
          • ==》如果没有[从presentation class到domain class]的关联性(linkQ,将domain class保存于presentation class的一个值域中?/li>
      • 针对GUI class内的domain dataQ?span style="font-weight: bold;">Self Encapsulate FieldQ?71Q?/li>
      • ~译Q测试?/li>
      • 在事件处理函敎ͼevent handlerQ中加上对设值函敎ͼsetterQ的调用Q以[直接讉K方式]更新GUIlg?/li>
          • ==》在事g处理函数中放一个设值函敎ͼsetterQ,利用它将GUIlg更新为domain data的当前倹{当然这其实没有必要Q你只不q是拿它的D定它自己。但是这样用setterQ便是允许其中的M动作得以于日后被执行hQ这是这一步骤的意义所在?/li>
          • ==》进行这个改变时Q对于组Ӟ不要使用取值函敎ͼgetterQ,应该采取[直接取用]方式Q因为稍后我们将修改取值函敎ͼgetterQ,使其从domain objectQ而非GUIlgQ取倹{设值函敎ͼsetterQ也遭受类g攏V?/li>
          • ==》确保测试代码能够触发新d的事件处理(event handlingQ机制?/li>
      • ~译Q测试?/li>
      • 在domain class中定义数据及其相兌问函敎ͼaccessorsQ?/li>
          • ==》确保domain class中的讑ր函敎ͼsetterQ能够触?span style="font-weight: bold;">Observer模式的通报机制Qnotify mechanismQ?/li>
          • ==》对于被观察Q被监视Q的数据Q在domain class中用[与presentation class所用的相同型别]Q通常是字W串Q来保存。后l重构中你可以自由改变这个数据型别?/li>
      • 修改presentation class中的讉K函数QaccessorsQ,它们的操作对象改ؓdomain objectQ而非GUIlgQ?/li>
      • 修改observer的updateQ)Q其从相应的domain object中将所需数据拯lGUIlg?/li>
      • ~译Q测试?br>


    ivaneeo 2005-09-07 12:19 发表评论
    ]]>
    重构?-重新l织数据QDuplicate Observed DataQ(4Q?-范例http://www.tkk7.com/ivanwan/archive/2005/09/06/12191.htmlivaneeoivaneeoTue, 06 Sep 2005 03:07:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/06/12191.htmlhttp://www.tkk7.com/ivanwan/comments/12191.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/06/12191.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/12191.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/12191.html范例QExamplesQ?br>我们的范例其行ؓ非常?当用户修Ҏ本框中的数?另两个文本框׃自动更新.如果你修改Start或End,length׃自动成ؓ两者计所得的长度;如果你修改length,End׃随之变动.

    一开?所有函数都攑֜IntervalWindow class?所有文本框都能够响应[失去键盘焦点]Qloss of focusQ这一事g?br> public class IntervalWindow extends Frame...
        java.awt.TextField _startField;
        java.awt.TextField _endField;
        java.awt.TextField _lengthField;

        class SymFocus extends java.awt.event.FocusAdapter
        {
           public void focusLost(java.awt.event.FocusEvent event)
           {
              Object object = event.getSource();
       
              if(object == _startField)
                 StartField_FocusLost(event);
              else if(object = _endField)
                 EndField_FocusLost(event);
              else if(object = _lengthField)
                 LengthField_FocusLost(event);
           }
    }

    ?span style="font-weight: bold;">Start文本框失ȝ点,事g监听器调用StartField_FocusLost()。另两个文本框的处理也类伹{事件处理函数大致如下:
    void StartField_FocusLost(java.awt.event.FocusEvent event) {
        if(isNotInteger(_startField.getText()))
           _startField.setText("0");
        calculateLength();
    }
    void EndField_FocusLost(java.awt.event.FocusEvent event) {
        if(isNotInteger(_endField.getText()))
           _endField.setText("0");
        calculateLength();
    }
    void LengthField_FocusLost(java.awt.event.FocusEvent event) {
        if(isNotInteger(_lengthField.getText()))
           _lengthField.setText("0");
        calculateLength();
    }

    如果文本框的字符串无法{换ؓ一个整敎ͼ那么该文本框的内容将变成0。而后Q调用相兌函敎ͼ
    void calculateLength() {
        try {
           int start = Integer.parseInt(_startField.getText());
           int end = Integer.parseInt(_endField.getText());
           int length = end - start;
           _lengthField.setText(String.valueOf(length));
        } catch(NumberFormatException e) {
           throw new RuntimeException("Unexpected Number Format Error");
        }
    }
    void calculateEnd() {
        try {
           int start = Integer.parseInt(_startField.getText());
           int end = Integer.parseInt(_endField.getText());
           int end = start + length;
           _endField.setText(String.valueOf(end));
        } catch(NumberFormatException e) {
           throw new RuntimeException("Unexpected Number Format Error");
        }
    }
    我的d是非视觉性的计算逻辑从GUI中分d来。基本上q就意味calculateLengthQ)和calculateEndQ)Ud一个独立的domain class厅Rؓ了这一目的Q我需要能够在不引用窗口类的前提取?span style="font-weight: bold;">Start?span style="font-weight: bold;">End?span style="font-weight: bold;">length三个文本框的倹{唯一办法是这些数据复制到domain class中,q保持与GUI class数据同步。这是Duplicate Observed DataQ?89Q的d?br>
    截至目前我还没有一个domain classQ所以我着手徏立一个:
    class Interval extends Observable {}

    IntervalWindow
    class需要与此崭新的domain class建立一个关联:
    private Interval _subject;

    然后Q我需要合理地初始化_subject值域Qƈ把IntervalWindow class变成Interval class的一?span style="font-weight: bold;">Observer。这很简单,只需把下列代码放qIntervalWindow构造函C可以了Q?br> _subject = new Interval();
    _subject.addObserver(this);
    update(_subject, null);

    我喜Ƣ把q段代码攑֜整个建构q程的最后。其中对updateQ)的调用可以确保:当我把数据复制到domain class后,GUI根?span style="font-weight: bold;">domain classq行初始化。updateQ)是在java.util.observer接口中声明的Q因此我必须?span style="font-weight: bold;">IntervalWindow class实现q一接口Q?br> public class IntervalWindow extends Frame implements Observer
    然后我还需要ؓIntervalWindow class建立一个updateQ)。此L先o它ؓI:
    public void update(Observable observed, Object arg)  {
    }
    现在我可以编译ƈ试了。到目前为止我还没有作出M真正的修攏V呵呵,心驶得万年舏V?br>
    接下来我把注意力转移到文本框。一如往常我每次只改动一点点。ؓ了卖弄一下我的英语能力,我从End文本框开始。第一件要做的事就是实?span style="font-weight: bold;">Self Encapsulate FieldQ?71Q。文本框的更新是通过getTextQ)和setTextQ)两函数实现的Q因此我所建立的访问函敎ͼaccessorsQ需要调用这两个函数Q?br>
    String getEnd() {
        return _endField.getText();
    }
    void setEnd(String arg) {
        _endField.setText(arg);
    }
    然后Q找出_endField的所有引用点Q将它们替换为适当的访问函敎ͼ
    void calculateLength() {
        try {
           int start = Integer.parseInt(_startField.getText());
           int end = Integer.parseInt(getEnd());
           int length = end - start;
           _lengthField.setText(String.valueOf(length));
        } catch(NumberFormatException e) {
           throw new RuntimeException("Unexpected Number Format Error");
        }
    }
    void calculateEnd() {
        try {
           int start = Integer.parseInt(_startField.getText());
           int end = Integer.parseInt(_endField.getText());
           int end = start + length;
           setEnd(String.valueOf(end));
        } catch(NumberFormatException e) {
           throw new RuntimeException("Unexpected Number Format Error");
        }
    }
    void EndField_FocusLost(java.awt.event.FocusEvent event) {
        if(isNotInteger(getEnd()))
           setEnd("0");
        calculateLength();
    }

    q是Self Encapsulate FieldQ?71Q的标准q程。然后当你处理GUI classӞ情况q更复杂些:用户可以直接Q通过GUIQ修Ҏ本框内容Q不必调用setEnd()。因此我需要在GUI class的事件处理函C加上对setEndQ)的调用。这个动作把End文本框设定ؓ其当前倹{当Ӟq没带来什么媄响,但是通过q样的方式,我们可以保用户的输入的是通过讑ր函敎ͼsetterQ进行的Q?br> void EndField_FocusLost(java.awt.event.FocusEvent event) {
        setEnd(_endField.getText());
        if(isNotInteger(getEnd()))

           setEnd("0");
        calculateLength();
    }

    上述调用动作中,我ƈ没有使用上一늚getEnd()取得End? 本框当前内容Q而是直接取用该文本框。之所以这样做是因为,随后的重构将使上一늚getEnd()从domain objectQ而非文本框)w上取倹{那时如果这里用的是getEndQ)函数Q每当用户修Ҏ本框内容Q这里就会将文本框又改回原倹{所以我必须使用 [直接讉K文本框]的方式获得当前倹{现在我可以~译q测试值域装后的行ؓ了?br>
    现在Q在domain class中加入_end值域Q?br style="background-color: rgb(211, 211, 211);"> class Interval...
        private String _end = "0";
    在这里,我给它的初始值和GUI classl它的初值是一L。然后我再加入取?讑ր函敎ͼgetter/setterQ:
    class Interval...
        String getEnd() {
           return _end;
        }
        void setEnd(String arg) {
           _end = arg;
           setChanged();
           notifyObservers();
        }

    ׃使用?span style="font-weight: bold;">Observer模式Q我必须在设值函敎ͼsetterQ? 中加上[发出通告]动作Q即所谓notification codeQ。我把_end声明Z个字W串Q而不是一个看似更合理的整敎ͼq是因ؓ我希望将修改量减x。将来成功复制数据完毕后Q我可以自由自在C domain class内部把_end声明为整数?br>
    现在Q我可以再编译ƈ试一ơ。我希望通过所有这些预备工作,下面这个较为棘手的重构步骤的风险降x低?br>
    首先Q修改IntervalWindow class的访问函敎ͼ令它们改用Interval对象Q?br> class IntervalWindow...
        String getEnd() {
           return _subject.getEnd();
        }
        void setEnd(String arg) {
           _subject.setEnd(arg);
        }
    同时也修改updateQ)函数Q确?span style="font-weight: bold;">GUI?span style="font-weight: bold;">Interval对象发来的通告做出响应Q?br> class IntervalWindow...
        public void update(Observable observed, Object arg) {
           _endField.setText(_subject.getEnd());
        }
    q是另一个需要[直接取用文本框]的地炏V如果我调用的是讑ր函敎ͼsetterQ,E序陷入无限递归调用Q这是因为IntervalWindow的设 值函数setEndQ)调用了Interval。setEndQ)Q一如稍早行所C:而Interval.setEnd()又调? notifyObserversQ)Q导致IntervalWindow.update()又被调用Q?br>
    现在Q我可以~译q测试,数据都恰如其分地被复制了?br>
    另两个文本框也如法炮制。完成之后,我可以用Move MethodQ?42Q将calculateEnd()和calculateLength()搬到Interval class。这么一来,我就拥有一个[包容所有domain behavior和domain data]q与GUI code分离的domain class了?br>
    如果上述工作都完成了Q我׃考虑d摆脱q个GUI class。如果GUI class是个较ؓ老旧的AWT classQ我会考虑它换成一个比较好看的Swing classQ而且后者的坐标定位能力也比较强。我可以在domain class之上建立一个Swing GUI。这P只要我高_随时可以L老旧的GUI class?br>
    使用事g监听器(Event ListenersQ?/span>

    如果你用事件监听器Qevent listenerQ而不?span style="font-weight: bold;">Observer/Observable模式Q仍然可以实?span style="font-weight: bold;">Duplicate Observed DataQ?89Q。这U情况下Q你需要在domain model中徏立一?span style="font-weight: bold;">listener class和一?span style="font-weight: bold;">event class。然后,你需要对domain object注册listenersQ就像前例对observable对象注册observers一栗每当domain object发生变化Q类g例的updateQ)函数被调用)Q就向listeners发送一个事ӞeventQ?span style="font-weight: bold;">IntervalWindow class可以利用一个inner classQ内嵌类Q来实现监听器接口(listener interfaceQ,q在适当时候调用适当的updateQ)函数?br>

    ivaneeo 2005-09-06 11:07 发表评论
    ]]>
    重构?-重新l织数据QDuplicate Observed DataQ(2Q?-动机http://www.tkk7.com/ivanwan/archive/2005/09/05/12052.htmlivaneeoivaneeoMon, 05 Sep 2005 09:04:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/05/12052.htmlhttp://www.tkk7.com/ivanwan/comments/12052.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/05/12052.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/12052.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/12052.html动机QMotivationQ?br> 一个分层良好的pȝQ应该将处理用户界面QUIQ和处理业务逻辑Qbusiness logicQ的代码分开。之所以这样做Q原因有以下几点Q(1Q你可能需要用数个不同的用户界面来表现相同的业务逻辑Q如果同时承担两U责任,用户界面 会变得过分复杂;Q?Q与GUI隔离之后Qdomain objects的维护和演化都会更容易;你甚臛_以让不同的开发者负责不同部分的开发?br>
    如果你遇到的代码是以双层Qtwo-tieredQ方式开发,业务逻辑被内嵌于用户界面QUI)之中Q你有必要行为分d来。其中的主要工作是函数的分d搬移。但数据׃同了Q你不能仅仅只是Ud数据Q你必须它复制到新建部位中Qƈ提供相应的同步机制?br>

    ivaneeo 2005-09-05 17:04 发表评论
    ]]>
    重构?-重新l织数据QDuplicate Observed DataQ(1Q?/title><link>http://www.tkk7.com/ivanwan/archive/2005/09/05/12051.html</link><dc:creator>ivaneeo</dc:creator><author>ivaneeo</author><pubDate>Mon, 05 Sep 2005 08:50:00 GMT</pubDate><guid>http://www.tkk7.com/ivanwan/archive/2005/09/05/12051.html</guid><wfw:comment>http://www.tkk7.com/ivanwan/comments/12051.html</wfw:comment><comments>http://www.tkk7.com/ivanwan/archive/2005/09/05/12051.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/ivanwan/comments/commentRss/12051.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/ivanwan/services/trackbacks/12051.html</trackback:ping><description><![CDATA[注:所谓presentation classQ用以处理[数据表现形式]Q所谓domain classQ用以处理业务逻辑?br> <br> 你有一些domain data|n于GUI控g中,而domain method需要访问之?br> <br> <span style="font-weight: bold;">该W数据拷贝一个domain object中。徏立一个Observer模式Q用以对domain object和GUI object内的重复数据q行同步控制Qsync.Q?br> <br> <img src="http://www.tkk7.com/images/blogjava_net/ivanwan/pictures/Duplicate%20Observed%20Data.png" alt="Duplicate Observed Data.png" border="0" height="433" width="797"><br> </span><img src ="http://www.tkk7.com/ivanwan/aggbug/12051.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/ivanwan/" target="_blank">ivaneeo</a> 2005-09-05 16:50 <a href="http://www.tkk7.com/ivanwan/archive/2005/09/05/12051.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>重构?-重新l织数据QReplace Array with ObjectQ(4Q?-范例http://www.tkk7.com/ivanwan/archive/2005/09/05/12039.htmlivaneeoivaneeoMon, 05 Sep 2005 07:09:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/05/12039.htmlhttp://www.tkk7.com/ivanwan/comments/12039.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/05/12039.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/12039.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/12039.html范例QExamplesQ?br> 我们的范例从一个数l开始,其中有三个元素,分别保存一支球队的名称、获胜场ơ和失利场次。这个数l的声明可能像这P
    String[] row = new String[3];
    客户端代码可能像q样Q?br style="background-color: rgb(211, 211, 211);"> row[0] = "Livepool";
    row[1] = "15";

    String name = row[0];
    int wins = Integer.parseInt(row[1]);

    Z数l变成对象,我首先徏立一个对应的classQ?br> class Performance{}
    然后为它声明一个public值域Q用以保存原先数l。(我知道public值域十恶不Q请攑ֿQ稍后我便让它改邪归正。)
    public String[] _data = new String[3];
    现在Q我要找到创建和讉K数组的地斏V在创徏地点Q我它替换Z列代码:
    Performance row = new Performance();
    对于数组使用地点Q我它替换Z下代码:
    row._data[0] = "Liverpool";
    row._data[1] = "15";

    String name = row._data[0];
    int wins = Integer.parseInt(row._data[1]);

    然后我要逐一为数l元素加上有意义的取?讑ր函敎ͼgetters/settersQ。首先从[球队名称]开始:
    class Performance...
        public String getName() {
           return _data[0];
        }
        public void setName(String arg) {
           _data[0] = arg;
        }
    然后修改row对象的用P让他们改用[取?讑ր函数]来访问球队名Uͼ
    row.setName("Liverpool");
    row._data[1] = "15";

    String name = row.getName();
    int wins = Integer.parseInt(row._data[1]);
    W二个元素也如法炮制。ؓ了简单v见,我还可以把数据型别的转换也封装v来:
    class Performance...
        public int getWins() {
           return Integer.parseInt(_data[1]);
        }
        public void setWins(String arg) {
           _data[1] = arg;
        }
    ...
    client code...
        row.setName("Liverpool");
        row.setWins("15");

        String name = row.getName();
        int wins = row.getWins();
    处理完所有元素之后,我就可以保存该数组的值域声明为private了?br> private String[] _data = new String[3];
    现在Q本ơ重构最重要的部分(接口修改Q已l完成。但是[对象内的数l替换掉]的过E也同样重要。我可以针对每个数组元素Q在class内徏立一个型别相当的值域Q然后修改该数组元素的访问函敎ͼ令它直接讉K新徏值域Q从而完全摆脱对数组元素的依赖?br> class Performance...
        public String getName() {
           return _name;
        }
        public void setName(String arg) {
           _name = arg;
        }
        private String _name;

    Ҏl中的每一个元素都如法炮制。全部处理完毕后Q我可以将数组从我?span style="font-weight: bold;">Performance class中删掉了?br>

    ivaneeo 2005-09-05 15:09 发表评论
    ]]>
    重构?-重新l织数据QReplace Array with ObjectQ(3Q?-作法http://www.tkk7.com/ivanwan/archive/2005/09/05/12026.htmlivaneeoivaneeoMon, 05 Sep 2005 05:49:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/05/12026.htmlhttp://www.tkk7.com/ivanwan/comments/12026.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/05/12026.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/12026.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/12026.html作法QMechanics)
      • 新徏一个class表示数组所CZ息,q在该class中以一个public值域保存原先的数l?/li>
      • 修改数组的所有用P让它们改用新建的class实体?/li>
      • ~译Q测试?/li>
      • 逐一为数l元素添加取?讑ր函敎ͼgetters/settersQ。根据元素的用途,些访问函数命名。修改客L代码Q让它们通过讉K函数取用数组内的元素。每ơ修改后Q编译ƈ试?/li>
      • 当所有[Ҏl的直接讉K]都被取代为[对访问函数的调用]后,class之中保存该数l的值域声明为private?/li>
      • ~译?/li>
      • 对于数组内的每一个元素,在新class中创Z个型别相当的值域Q修改该元素的访问函敎ͼ令它改用上述的新建值域?/li>
      • 每修改一个元素,~译q测试?/li>
      • 数组的所有元素都在对应的class内有了相应值域之后Q删除该数组?br>


    ivaneeo 2005-09-05 13:49 发表评论
    ]]>
    重构?-重新l织数据QReplace Array with ObjectQ(2Q?-动机http://www.tkk7.com/ivanwan/archive/2005/09/05/12022.htmlivaneeoivaneeoMon, 05 Sep 2005 05:30:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/05/12022.htmlhttp://www.tkk7.com/ivanwan/comments/12022.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/05/12022.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/12022.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/12022.html动机QMotivationQ?br> 数组QarrayQ是一U常见的用以l织数据的结构体。不q,它们应该只用于[以某U顺序容U一l相似对象]。有时侯你会发现Q一个数l容U了数种不同? 象,q会larray用户带来ȝQ因Z们很难记住像[数组的第一个元素是人名]q样的约定。对象就不同了,你可以运用值域名称和函数名U来传达q样? 信息Q因此你不需死记它,也无需依赖注释。而且如果使用对象Q你q可以将信息装hQƈ使用Move MethodQ?42Qؓ它加上相兌为?br>

    ivaneeo 2005-09-05 13:30 发表评论
    ]]>
    重构?-重新l织数据QReplace Array with ObjectQ(1Q?/title><link>http://www.tkk7.com/ivanwan/archive/2005/09/05/12021.html</link><dc:creator>ivaneeo</dc:creator><author>ivaneeo</author><pubDate>Mon, 05 Sep 2005 05:22:00 GMT</pubDate><guid>http://www.tkk7.com/ivanwan/archive/2005/09/05/12021.html</guid><wfw:comment>http://www.tkk7.com/ivanwan/comments/12021.html</wfw:comment><comments>http://www.tkk7.com/ivanwan/archive/2005/09/05/12021.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/ivanwan/comments/commentRss/12021.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/ivanwan/services/trackbacks/12021.html</trackback:ping><description><![CDATA[你有一个数l(arrayQ,其中的元素各自代表不同的东西?br> <br> <span style="font-weight: bold;">以对象替换数l。对于数l中的每个元素,以一个值域表示之?/span><br style="background-color: rgb(211, 211, 211);"> <br style="background-color: rgb(211, 211, 211);"> <span style="background-color: rgb(211, 211, 211);">String[] row = new String[3];</span><br style="background-color: rgb(211, 211, 211);"> <span style="background-color: rgb(211, 211, 211);">row[0] = "Livepool";</span><br style="background-color: rgb(211, 211, 211);"> <span style="background-color: rgb(211, 211, 211);">row[1] = "15";<br> <span style="background-color: rgb(255, 255, 255);">                                <img src="http://www.tkk7.com/images/blogjava_net/ivanwan/pictures/126.gif" alt="126.gif" border="0" height="41" width="45"><br style="background-color: rgb(211, 211, 211);"> <span style="background-color: rgb(211, 211, 211);">Performance row = new Performance();</span><br style="background-color: rgb(211, 211, 211);"> <span style="background-color: rgb(211, 211, 211);">row.setName("Livepool");</span><br style="background-color: rgb(211, 211, 211);"> <span style="background-color: rgb(211, 211, 211);">row.setWins("15");</span><br> </span></span><span style="font-weight: bold;"></span><img src ="http://www.tkk7.com/ivanwan/aggbug/12021.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/ivanwan/" target="_blank">ivaneeo</a> 2005-09-05 13:22 <a href="http://www.tkk7.com/ivanwan/archive/2005/09/05/12021.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>重构?-重新l织数据QChange Reference to ValueQ(4Q?-范例http://www.tkk7.com/ivanwan/archive/2005/09/05/12008.htmlivaneeoivaneeoMon, 05 Sep 2005 03:04:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/05/12008.htmlhttp://www.tkk7.com/ivanwan/comments/12008.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/05/12008.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/12008.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/12008.html范例QExamplesQ?br> 让我们从一个表C[货币U类]的Currency class开始:
    class Currency...
        private String _code;

        public String getCode() {
           return _code;
        }

        private Currency(String code) {
           _code = code;
        }
    q个class所做的是保存q返回一个货币种cM码。它是一个reference objectQ所以如果要得到它的一份实体,必须q么做:
        Currency usd = Currency.get("USD");
    Currency classl护一个实体链表(list of instancesQ;我不能直接用构造函数创建实体,因ؓCurrency构造函数是private?br> new Currency("USD").equals(new Currency("USD"));   //return false

    要把一?span style="font-weight: bold;">reference object变成value objectQ关键动作是Q检查它是否为immutableQ不可变Q。如果不是,我就不能使用本项重构Q因为mutableQ可变的Qvalue object会造成令h苦恼的别名现象(aliasingQ?br>
    在这里,Currency对象是不可变的,所以下一步就是ؓ它定义equalsQ)Q?br> public boolean equals(Object arg) {
        if(!(arg instanceof Currency)) return false;
        Currency other = (Currency)arg;
        return (_code.equals(other._code));
    }

    如果我定义equalsQ)Q我必须同时定义hashCodeQ)。实现hashCodeQ)有个单办法:dequalsQ)使用的所有值域的hash codesQ然后对它们q行bitwise xorQ^Q操作。本例中q很Ҏ实现Q因为equalsQ)只用了一个值域Q?br> public int hashCode() {
        return _code.hashCode():
    }
    完成q两个函数后Q我可以~译q测试。这两个函数的修改必d时进行,否则依赖hashing的Q何群集对象(collectionsQ例如Hashtable、HashSet和HashMapQ可能会产生意外行ؓ?br>
    现在Q我惛_建多个{值的Currency对象创建多个。我q可以把构造函数声明ؓpublicQ直接以构造函数获取Currency实体Q从而去掉Currency class中的factory method和[控制实体创徏]的行为:
    new Currency("USD").equals(new Currency("USD"));   //now returns true


    ivaneeo 2005-09-05 11:04 发表评论
    ]]>
    重构?-重新l织数据QChange Reference to ValueQ(3Q?-作法http://www.tkk7.com/ivanwan/archive/2005/09/05/12006.htmlivaneeoivaneeoMon, 05 Sep 2005 02:46:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/05/12006.htmlhttp://www.tkk7.com/ivanwan/comments/12006.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/05/12006.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/12006.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/12006.html作法QMechanics)
      • 查重构对象是否ؓimmutableQ不可变Q对象,或是否可修改Z可变对象?/li>
          • ==》如果该对象目前q是immutableQ就使用Remove Setting MethodQ?00Q,直到它成为immutable为止?/li>
          • 如果无法该对象修改为immutableQ就攑ּ使用本项重构?/li>
      • 建立equalsQ)和hashCodeQ)?/li>
      • ~译Q测试?/li>
      • 考虑是否可以删除factory methodQƈ构造函数声明ؓpublic?br>


    ivaneeo 2005-09-05 10:46 发表评论
    ]]>
    重构?-重新l织数据QChange Reference to valueQ(2Q?-动机 http://www.tkk7.com/ivanwan/archive/2005/09/05/12005.htmlivaneeoivaneeoMon, 05 Sep 2005 02:34:00 GMThttp://www.tkk7.com/ivanwan/archive/2005/09/05/12005.htmlhttp://www.tkk7.com/ivanwan/comments/12005.htmlhttp://www.tkk7.com/ivanwan/archive/2005/09/05/12005.html#Feedback0http://www.tkk7.com/ivanwan/comments/commentRss/12005.htmlhttp://www.tkk7.com/ivanwan/services/trackbacks/12005.html动机QMotivationQ?br> 在分布系l和q发pȝ中,不可变的value object特别有用Q因Z不须考虑它们的同步问题?br>
    value object有一个非帔R要的Ҏ:它们应该是不可变的(immutableQ。无Z时只要你调用同一个对象的同一个查询函敎ͼ你都应该得到同样l果。如果保证了q一点,可以放心地以多个对象表C相同事物(same thingQ。如?span style="font-weight: bold;">value object是可变的QmutableQ,你就必须保你对某一对象的修改会自动更新其他[代表同事物]的其他对象。这太痛苦了Q与其如此还不如把它变成reference object?br>
    q里有必要澄清一下[不可变(immutableQ]的意思。如果你?span style="font-weight: bold;">Money class表示[钱]的概念,其中有[币种]和[金额]两条信息Q那?span style="font-weight: bold;">Money对象通常是一个不可变?span style="font-weight: bold;">value object。这q意味你的薪资不能改变Q而是意味Q如果要改变你的薪资Q你需要用另一个崭新的Money对象来取代先有的Money对象Q而不是在现有?span style="font-weight: bold;">Money对象上修攏V你?span style="font-weight: bold;">Money对象之间的关pd以改变,?span style="font-weight: bold;">Money对象自n不能改变?img src ="http://www.tkk7.com/ivanwan/aggbug/12005.html" width = "1" height = "1" />

    ivaneeo 2005-09-05 10:34 发表评论
    ]]>
    重构?-重新l织数据QChange Reference to ValueQ(1Q?/title><link>http://www.tkk7.com/ivanwan/archive/2005/09/05/12003.html</link><dc:creator>ivaneeo</dc:creator><author>ivaneeo</author><pubDate>Mon, 05 Sep 2005 01:47:00 GMT</pubDate><guid>http://www.tkk7.com/ivanwan/archive/2005/09/05/12003.html</guid><wfw:comment>http://www.tkk7.com/ivanwan/comments/12003.html</wfw:comment><comments>http://www.tkk7.com/ivanwan/archive/2005/09/05/12003.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/ivanwan/comments/commentRss/12003.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/ivanwan/services/trackbacks/12003.html</trackback:ping><description><![CDATA[你有一?span style="font-weight: bold;">reference</span> objectQ引用对象)Q很且不可变(immutableQ,而且不易理?br style="font-weight: bold;"> <br style="font-weight: bold;"> <span style="font-weight: bold;">它变成一个value</span> object<span style="font-weight: bold;">Q实值对象)?br> <br> <img src="http://www.tkk7.com/images/blogjava_net/ivanwan/pictures/Change%20Reference%20to%20Value.png" alt="Change Reference to Value.png" border="0" height="291" width="449"><br> </span><img src ="http://www.tkk7.com/ivanwan/aggbug/12003.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/ivanwan/" target="_blank">ivaneeo</a> 2005-09-05 09:47 <a href="http://www.tkk7.com/ivanwan/archive/2005/09/05/12003.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.tkk7.com/" title="亚洲av成人片在线观看">亚洲av成人片在线观看</a> <div class="friend-links"> </div> </div> </footer> վ֩ģ壺 <a href="http://woaisouluo.com" target="_blank">޾Ʒþþþþ</a>| <a href="http://rr7733.com" target="_blank">þþþ뾫Ʒպ</a>| <a href="http://chinahongfeng.com" target="_blank">һ˿Ƶ</a>| <a href="http://woaisouluo.com" target="_blank">avƬ߹ۿ18Ů</a>| <a href="http://zgdhuibao.com" target="_blank">xxxxxƬƵ</a>| <a href="http://j2eesp.com" target="_blank">Ļ޳AƬ </a>| <a href="http://cdkunyu.com" target="_blank">һɫþۺ޾Ʒ</a>| <a href="http://kj555888.com" target="_blank">޵Ƶѹۿ1000</a>| <a href="http://770144.com" target="_blank">һĻ</a>| <a href="http://vvbbn.com" target="_blank">gvվ</a>| <a href="http://wo93xyz.com" target="_blank">ѿhƬվ</a>| <a href="http://ywjh666.com" target="_blank">һaëƬƵ</a>| <a href="http://dqcjlb.com" target="_blank">õӰ߹ۿ</a>| <a href="http://xjyzz.com" target="_blank">˳ӰԺ߹ۿ</a>| <a href="http://69xjk.com" target="_blank">þþ޾ƷƵ</a>| <a href="http://732r.com" target="_blank">Ů˿ƬƵ</a>| <a href="http://www132126.com" target="_blank">Ʒ69XXXƵ</a>| <a href="http://jinlaifubuxiugang.com" target="_blank">͵ͼƬ</a>| <a href="http://z88d.com" target="_blank">޾Ʒھþ </a>| <a href="http://peipeixiu.com" target="_blank">߲</a>| <a href="http://cctv69.com" target="_blank">һƵ </a>| <a href="http://ulihix.com" target="_blank">˳77777߹ۿ</a>| <a href="http://yongyihongze.com" target="_blank">Ƶ</a>| <a href="http://0359jgyy.com" target="_blank">˻ƶվƵ</a>| <a href="http://ksyanhui.com" target="_blank">պƷרҹ </a>| <a href="http://jack-fx.com" target="_blank">99Ʒ</a>| <a href="http://av56cc.com" target="_blank">vƬ߹ۿ</a>| <a href="http://m0808dy.com" target="_blank">ɫ͵͵ۺAV</a>| <a href="http://www6yg6yg.com" target="_blank">þþƷһ</a>| <a href="http://dcqzr.com" target="_blank">ѹҹ</a>| <a href="http://wwwnewhtbook.com" target="_blank">ɫ͵͵޵һۺ</a>| <a href="http://pj9xx6.com" target="_blank">쾫Ʒ߹ۿ</a>| <a href="http://yakonet.com" target="_blank">þӰӹ</a>| <a href="http://ygf123.com" target="_blank">ѿһػaaƬ</a>| <a href="http://tianmao920.com" target="_blank">97ѹۿƵ߹ۿ</a>| <a href="http://2023852.com" target="_blank">ó˾þAvѸ </a>| <a href="http://733807.com" target="_blank">޹һ</a>| <a href="http://www84847.com" target="_blank">Ļ޵һ</a>| <a href="http://lzlcp.com" target="_blank">ѿbbb</a>| <a href="http://9v9av.com" target="_blank">Ʒþһ</a>| <a href="http://xbooktxt.com" target="_blank">þù߳׽ѹۿ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>