<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    ivaneeo's blog

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

      BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
      669 Posts :: 0 Stories :: 64 Comments :: 0 Trackbacks
    范例(Examples)
    我們的范例其行為非常簡單:當(dāng)用戶修改文本框中的數(shù)值,另兩個(gè)文本框就會(huì)自動(dòng)更新.如果你修改Start或End,length就會(huì)自動(dòng)成為兩者計(jì)算所得的長度;如果你修改length,End就會(huì)隨之變動(dòng).

    一開始,所有函數(shù)都放在IntervalWindow class中.所有文本框都能夠響應(yīng)[失去鍵盤焦點(diǎn)](loss of focus)這一事件。
    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);
    ??? ?? }
    }

    當(dāng)Start文本框失去焦點(diǎn),事件監(jiān)聽器調(diào)用StartField_FocusLost()。另兩個(gè)文本框的處理也類似。事件處理函數(shù)大致如下:
    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();
    }

    如果文本框的字符串無法轉(zhuǎn)換為一個(gè)整數(shù),那么該文本框的內(nèi)容將變成0。而后,調(diào)用相關(guān)計(jì)算函數(shù):
    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");
    ??? }
    }
    我的任務(wù)就是非視覺性的計(jì)算邏輯從GUI中分離出來。基本上這就意味將calculateLength()和calculateEnd()移到一個(gè)獨(dú)立的domain class去。為了這一目的,我需要能夠在不引用窗口類的前提取用StartEndlength三個(gè)文本框的值。唯一辦法就是將這些數(shù)據(jù)復(fù)制到domain class中,并保持與GUI class數(shù)據(jù)同步。這就是Duplicate Observed Data(189)的任務(wù)。

    截至目前我還沒有一個(gè)domain class,所以我著手建立一個(gè):
    class Interval extends Observable {}

    IntervalWindow
    class需要與此嶄新的domain class建立一個(gè)關(guān)聯(lián):
    private Interval _subject;

    然后,我需要合理地初始化_subject值域,并把IntervalWindow class變成Interval class的一個(gè)Observer。這很簡單,只需把下列代碼放進(jìn)IntervalWindow構(gòu)造函數(shù)中就可以了:
    _subject = new Interval();
    _subject.addObserver(this);
    update(_subject, null);

    我喜歡把這段代碼放在整個(gè)建構(gòu)過程的最后。其中對update()的調(diào)用可以確保:當(dāng)我把數(shù)據(jù)復(fù)制到domain class后,GUI將根據(jù)domain class進(jìn)行初始化。update()是在java.util.observer接口中聲明的,因此我必須讓IntervalWindow class實(shí)現(xiàn)這一接口:
    public class IntervalWindow extends Frame implements Observer
    然后我還需要為IntervalWindow class建立一個(gè)update()。此刻我先令它為空:
    public void update(Observable observed, Object arg)? {
    }
    現(xiàn)在我可以編譯并測試了。到目前為止我還沒有作出任何真正的修改。呵呵,小心駛得萬年船。

    接下來我把注意力轉(zhuǎn)移到文本框。一如往常我每次只改動(dòng)一點(diǎn)點(diǎn)。為了賣弄一下我的英語能力,我從End文本框開始。第一件要做的事就是實(shí)施Self Encapsulate Field(171)。文本框的更新是通過getText()和setText()兩函數(shù)實(shí)現(xiàn)的,因此我所建立的訪問函數(shù)(accessors)需要調(diào)用這兩個(gè)函數(shù):

    String getEnd() {
    ??? return _endField.getText();
    }
    void setEnd(String arg) {
    ??? _endField.setText(arg);
    }
    然后,找出_endField的所有引用點(diǎn),將它們替換為適當(dāng)?shù)脑L問函數(shù):
    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();
    }

    這是Self Encapsulate Field(171)的標(biāo)準(zhǔn)過程。然后當(dāng)你處理GUI class時(shí),情況還更復(fù)雜些:用戶可以直接(通過GUI)修改文本框內(nèi)容,不必調(diào)用setEnd()。因此我需要在GUI class的事件處理函數(shù)中加上對setEnd()的調(diào)用。這個(gè)動(dòng)作把End文本框設(shè)定為其當(dāng)前值。當(dāng)然,這沒帶來什么影響,但是通過這樣的方式,我們可以確保用戶的輸入的確是通過設(shè)值函數(shù)(setter)進(jìn)行的:
    void EndField_FocusLost(java.awt.event.FocusEvent event) {
    ??? setEnd(_endField.getText());
    ??? if(isNotInteger(getEnd()))

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

    上述調(diào)用動(dòng)作中,我并沒有使用上一頁的getEnd()取得End文 本框當(dāng)前內(nèi)容,而是直接取用該文本框。之所以這樣做是因?yàn)椋S后的重構(gòu)將使上一頁的getEnd()從domain object(而非文本框)身上取值。那時(shí)如果這里用的是getEnd()函數(shù),每當(dāng)用戶修改文本框內(nèi)容,這里就會(huì)將文本框又改回原值。所以我必須使用 [直接訪問文本框]的方式獲得當(dāng)前值。現(xiàn)在我可以編譯并測試值域封裝后的行為了。

    現(xiàn)在,在domain class中加入_end值域:
    class Interval...
    ??? private String _end = "0";
    在這里,我給它的初始值和GUI class給它的初值是一樣的。然后我再加入取值/設(shè)值函數(shù)(getter/setter):
    class Interval...
    ??? String getEnd() {
    ??? ?? return _end;
    ??? }
    ??? void setEnd(String arg) {
    ??? ?? _end = arg;
    ??? ?? setChanged();
    ??? ?? notifyObservers();
    ??? }

    由于使用了Observer模式,我必須在設(shè)值函數(shù)(setter) 中加上[發(fā)出通告]動(dòng)作(即所謂notification code)。我把_end聲明為一個(gè)字符串,而不是一個(gè)看似更合理的整數(shù),這是因?yàn)槲蚁M麑⑿薷牧繙p至最少。將來成功復(fù)制數(shù)據(jù)完畢后,我可以自由自在地于 domain class內(nèi)部把_end聲明為整數(shù)。

    現(xiàn)在,我可以再編譯并測試一次。我希望通過所有這些預(yù)備工作,將下面這個(gè)較為棘手的重構(gòu)步驟的風(fēng)險(xiǎn)降至最低。

    首先,修改IntervalWindow class的訪問函數(shù),令它們改用Interval對象:
    class IntervalWindow...
    ??? String getEnd() {
    ??? ?? return _subject.getEnd();
    ??? }
    ??? void setEnd(String arg) {
    ??? ?? _subject.setEnd(arg);
    ??? }
    同時(shí)也修改update()函數(shù),確保GUIInterval對象發(fā)來的通告做出響應(yīng):
    class IntervalWindow...
    ??? public void update(Observable observed, Object arg) {
    ??? ?? _endField.setText(_subject.getEnd());
    ??? }
    這是另一個(gè)需要[直接取用文本框]的地點(diǎn)。如果我調(diào)用的是設(shè)值函數(shù)(setter),程序?qū)⑾萑霟o限遞歸調(diào)用(這是因?yàn)镮ntervalWindow的設(shè) 值函數(shù)setEnd()調(diào)用了Interval。setEnd(),一如稍早行所示:而Interval.setEnd()又調(diào)用 notifyObservers(),導(dǎo)致IntervalWindow.update()又被調(diào)用)。

    現(xiàn)在,我可以編譯并測試,數(shù)據(jù)都恰如其分地被復(fù)制了。

    另兩個(gè)文本框也如法炮制。完成之后,我可以使用Move Method(142)將calculateEnd()和calculateLength()搬到Interval class。這么一來,我就擁有一個(gè)[包容所有domain behavior和domain data]并與GUI code分離的domain class了。

    如果上述工作都完成了,我就會(huì)考慮徹底擺脫這個(gè)GUI class。如果GUI class是個(gè)較為老舊的AWT class,我會(huì)考慮將它換成一個(gè)比較好看的Swing class,而且后者的坐標(biāo)定位能力也比較強(qiáng)。我可以在domain class之上建立一個(gè)Swing GUI。這樣,只要我高興,隨時(shí)可以去掉老舊的GUI class。

    使用事件監(jiān)聽器(Event Listeners)

    如果你使用事件監(jiān)聽器(event listener)而不是Observer/Observable模式,仍然可以實(shí)施Duplicate Observed Data(189)。這種情況下,你需要在domain model中建立一個(gè)listener class和一個(gè)event class。然后,你需要對domain object注冊listeners,就像前例對observable對象注冊observers一樣。每當(dāng)domain object發(fā)生變化(類似上例的update()函數(shù)被調(diào)用),就向listeners發(fā)送一個(gè)事件(event)。IntervalWindow class可以利用一個(gè)inner class(內(nèi)嵌類)來實(shí)現(xiàn)監(jiān)聽器接口(listener interface),并在適當(dāng)時(shí)候調(diào)用適當(dāng)?shù)膗pdate()函數(shù)。
    posted on 2005-09-06 11:07 ivaneeo 閱讀(604) 評論(0)  編輯  收藏 所屬分類: refactoring-從地獄中重生
    主站蜘蛛池模板: 国产大片51精品免费观看| 婷婷亚洲综合一区二区| 久久影院亚洲一区| 四虎影院在线免费播放| 亚洲国产高清在线| 亚洲成网777777国产精品| 特a级免费高清黄色片| 亚洲三级高清免费| 免费在线观看的黄色网址| AV片在线观看免费| 国产92成人精品视频免费| 久久精品中文字幕免费| 国产黄在线播放免费观看| 看一级毛片免费观看视频| 亚洲精品av无码喷奶水糖心| 亚洲国产高清在线精品一区 | 极品色天使在线婷婷天堂亚洲| 亚洲精品免费在线视频| 水蜜桃亚洲一二三四在线| 在线观看免费宅男视频| 亚色九九九全国免费视频| 亚洲人成免费电影| 一级毛片成人免费看免费不卡| a毛片免费在线观看| 亚洲中文字幕无码久久| 久久精品国产69国产精品亚洲| 成人激情免费视频| 在线观看无码AV网站永久免费 | 久久精品亚洲精品国产色婷| 久久精品国产精品亚洲艾| 国产亚洲无线码一区二区| 亚洲国产成人高清在线观看| 亚洲人成人一区二区三区| 精品久久久久久亚洲| 亚洲VA中文字幕不卡无码| 色噜噜综合亚洲av中文无码| 久久精品国产亚洲av影院| 亚洲国产精品综合一区在线| 亚洲综合色丁香婷婷六月图片| 亚洲精品GV天堂无码男同| 牛牛在线精品观看免费正 |