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

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

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

    ivaneeo's blog

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

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

    一開(kāi)始,所有函數(shù)都放在IntervalWindow class中.所有文本框都能夠響應(yīng)[失去鍵盤(pán)焦點(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)聽(tīng)器調(diào)用StartField_FocusLost()。另兩個(gè)文本框的處理也類(lèi)似。事件處理函數(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();
    }

    如果文本框的字符串無(wú)法轉(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ù)就是非視覺(jué)性的計(jì)算邏輯從GUI中分離出來(lái)。基本上這就意味將calculateLength()和calculateEnd()移到一個(gè)獨(dú)立的domain class去。為了這一目的,我需要能夠在不引用窗口類(lèi)的前提取用StartEndlength三個(gè)文本框的值。唯一辦法就是將這些數(shù)據(jù)復(fù)制到domain class中,并保持與GUI class數(shù)據(jù)同步。這就是Duplicate Observed Data(189)的任務(wù)。

    截至目前我還沒(méi)有一個(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。這很簡(jiǎn)單,只需把下列代碼放進(jìn)IntervalWindow構(gòu)造函數(shù)中就可以了:
    _subject = new Interval();
    _subject.addObserver(this);
    update(_subject, null);

    我喜歡把這段代碼放在整個(gè)建構(gòu)過(guò)程的最后。其中對(duì)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)在我可以編譯并測(cè)試了。到目前為止我還沒(méi)有作出任何真正的修改。呵呵,小心駛得萬(wàn)年船。

    接下來(lái)我把注意力轉(zhuǎn)移到文本框。一如往常我每次只改動(dòng)一點(diǎn)點(diǎn)。為了賣(mài)弄一下我的英語(yǔ)能力,我從End文本框開(kāi)始。第一件要做的事就是實(shí)施Self Encapsulate Field(171)。文本框的更新是通過(guò)getText()和setText()兩函數(shù)實(shí)現(xiàn)的,因此我所建立的訪問(wè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問(wèn)函數(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)過(guò)程。然后當(dāng)你處理GUI class時(shí),情況還更復(fù)雜些:用戶可以直接(通過(guò)GUI)修改文本框內(nèi)容,不必調(diào)用setEnd()。因此我需要在GUI class的事件處理函數(shù)中加上對(duì)setEnd()的調(diào)用。這個(gè)動(dòng)作把End文本框設(shè)定為其當(dāng)前值。當(dāng)然,這沒(méi)帶來(lái)什么影響,但是通過(guò)這樣的方式,我們可以確保用戶的輸入的確是通過(guò)設(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)作中,我并沒(méi)有使用上一頁(yè)的getEnd()取得End文 本框當(dāng)前內(nèi)容,而是直接取用該文本框。之所以這樣做是因?yàn)椋S后的重構(gòu)將使上一頁(yè)的getEnd()從domain object(而非文本框)身上取值。那時(shí)如果這里用的是getEnd()函數(shù),每當(dāng)用戶修改文本框內(nèi)容,這里就會(huì)將文本框又改回原值。所以我必須使用 [直接訪問(wèn)文本框]的方式獲得當(dāng)前值。現(xiàn)在我可以編譯并測(cè)試值域封裝后的行為了。

    現(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至最少。將來(lái)成功復(fù)制數(shù)據(jù)完畢后,我可以自由自在地于 domain class內(nèi)部把_end聲明為整數(shù)。

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

    首先,修改IntervalWindow class的訪問(wèn)函數(shù),令它們改用Interval對(duì)象:
    class IntervalWindow...
    ??? String getEnd() {
    ??? ?? return _subject.getEnd();
    ??? }
    ??? void setEnd(String arg) {
    ??? ?? _subject.setEnd(arg);
    ??? }
    同時(shí)也修改update()函數(shù),確保GUI對(duì)Interval對(duì)象發(fā)來(lái)的通告做出響應(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(wú)限遞歸調(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)在,我可以編譯并測(cè)試,數(shù)據(jù)都恰如其分地被復(fù)制了。

    另兩個(gè)文本框也如法炮制。完成之后,我可以使用Move Method(142)將calculateEnd()和calculateLength()搬到Interval class。這么一來(lái),我就擁有一個(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)聽(tīng)器(Event Listeners)

    如果你使用事件監(jiān)聽(tīng)器(event listener)而不是Observer/Observable模式,仍然可以實(shí)施Duplicate Observed Data(189)。這種情況下,你需要在domain model中建立一個(gè)listener class和一個(gè)event class。然后,你需要對(duì)domain object注冊(cè)listeners,就像前例對(duì)observable對(duì)象注冊(cè)observers一樣。每當(dāng)domain object發(fā)生變化(類(lèi)似上例的update()函數(shù)被調(diào)用),就向listeners發(fā)送一個(gè)事件(event)。IntervalWindow class可以利用一個(gè)inner class(內(nèi)嵌類(lèi))來(lái)實(shí)現(xiàn)監(jiān)聽(tīng)器接口(listener interface),并在適當(dāng)時(shí)候調(diào)用適當(dāng)?shù)膗pdate()函數(shù)。
    posted on 2005-09-06 11:07 ivaneeo 閱讀(604) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): refactoring-從地獄中重生
    主站蜘蛛池模板: 亚洲精品视频在线免费| 免费国产污网站在线观看| 久久亚洲精品无码观看不卡| 日本zzzzwww大片免费| 一区二区三区免费视频网站| 亚洲w码欧洲s码免费| 九月丁香婷婷亚洲综合色| 国产成人免费手机在线观看视频 | 亚洲精品中文字幕无乱码麻豆| 91麻豆精品国产自产在线观看亚洲| 毛片免费全部播放一级| 久久免费视频精品| 中文字幕在线观看免费| 国产成人精品日本亚洲语音| 亚洲天堂免费在线| 亚洲精品人成网在线播放影院| 亚洲精品一区二区三区四区乱码 | 在线看片无码永久免费aⅴ| 亚洲精品视频在线观看免费| 十八禁在线观看视频播放免费| a级成人免费毛片完整版| 一级黄色毛片免费看| 两个人日本免费完整版在线观看1| 你懂的在线免费观看| 久久久久久国产精品免费无码| 爽爽爽爽爽爽爽成人免费观看| 久久精品乱子伦免费| 69式国产真人免费视频| 2020因为爱你带字幕免费观看全集 | 亚洲精品国产福利在线观看| 中文字幕乱码亚洲精品一区| 黄色网址大全免费| 国产AV日韩A∨亚洲AV电影| 亚洲国产成人精品无码区二本| 亚洲中文无码永久免费| 精品亚洲AV无码一区二区| 亚洲第一街区偷拍街拍| 亚洲天堂免费在线视频| 99久久精品毛片免费播放| 18女人腿打开无遮掩免费| 国产精品冒白浆免费视频|