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

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

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

    treenode

    在路上。

    BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
      5 Posts :: 1 Stories :: 53 Comments :: 0 Trackbacks

    2006年12月1日 #


    軟件業(yè)總是充滿了形形色色的隱喻。比如說,把程序中的問題稱為bug;把互聯(lián)網(wǎng)上傳播的病毒叫做蠕蟲;
    把軟件開發(fā)的過程比作造房子......這些都是我們這個行業(yè)中流行的隱喻,以至于它們已經(jīng)成為軟件開
    發(fā)者文化中一個特有的組成部分。

    在這里,我想要說的是一個特別具有“中國特色”的隱喻:我稱之為武俠隱喻。

    毋庸我多做解釋,可能很多程序員看到這個詞就足以勾起豐富的想象。我們中的很多人喜歡把自己所崇敬
    和佩服的、軟件界有影響力的人稱之為大俠,并幻想自己有朝一日能夠達到他們的境界。(跑題一下,
    這個稱謂現(xiàn)在似乎有了一個比較草根的、或者說比較Web2.0的版本——叫做牛人)上個世紀那個個人
    英雄主義的年代,曾經(jīng)涌現(xiàn)出一大批這樣的人物,現(xiàn)在很多人仍然習慣稱他們?yōu)椤按髠b”——這其
    中包括求伯君、王志東、鮑岳橋、朱崇君...

    除此之外,我們還喜歡將重量級的出版物稱之為武林秘籍;把軟件開發(fā)的組織團體比作江湖幫派;
    要形容軟件開發(fā)的理想境界,也常會搬出“飛花摘葉俱可傷人”或“無劍勝有劍”這樣的句子。所有這些
    都或多或少的表明:武俠深深影響了大量的程序員,他們非常喜歡用武俠中的理念來比喻軟件開發(fā)過程中
    的現(xiàn)象。或許,也是因為程序員0和1的生活太過枯燥,需要文化來加一點味道,而他們自覺不自覺的
    選擇了武俠。這就是所謂的中國特色吧。

    我必須老實的承認:我自己就曾經(jīng)深受武俠的影響,過去也一直沒有感到有什么不妥。但是,
    在前幾天看過網(wǎng)上的某些回帖中一些充滿武俠隱喻味道的文字,突然覺得有些不是滋味。
    我開始思考:對于軟件開發(fā)來說,武俠是不是一個好的隱喻?結(jié)論:不是不好,而是非常的
    糟。武俠和軟件開發(fā)根本沒有什么共同點,甚至可以說是水火不容的。

    為什么說武俠和軟件開發(fā)沒有共同點?武俠講的是破壞的藝術(shù)。太史公說“俠以武犯禁”。
    武俠的意義,在最好的情況下,也僅僅是殺富濟貧、除暴安良,是對舊有秩序的破壞。
    問題在于:破壞是痛快愜意的,但破而不立就是純粹的破壞,沒有任何積極意義。
    破壞以后新的秩序如何建立呢?沒有哪一個武俠故事為此做出答案,
    也沒有一個俠客操心這種事。他們在乎的是“十步殺一人,千里不留形”的高手形象,
    至于走了以后爛攤子誰來收拾?那本大俠可就管不著了。

    軟件開發(fā)是建設(shè),而不是
    破壞。即使舊的系統(tǒng)非常糟糕,我們也沒有理由將其付之一炬——這就是為什么現(xiàn)代的敏捷
    開發(fā)者非常強調(diào)重構(gòu)的原因。構(gòu)造新的代碼固然是極具創(chuàng)造快感的工作,但是軟件開發(fā)過
    程中還有成打的“骯臟”工作:需求分析,設(shè)計,文檔,調(diào)試,維護......這些工作繁冗
    而瑣碎,但卻是整個開發(fā)過程中必不可少的組成部分。想瀟灑一下就拍拍屁股走人的
    程序員沒有什么職業(yè)素質(zhì)可言。

    武俠中的高手是什么形象?天馬行空,獨往獨來,神出鬼沒。這樣的人看起來很有性格,
    但在現(xiàn)代企業(yè)中恰恰是最忌諱的。而正正經(jīng)經(jīng)提倡Team Work的團隊反倒在武俠中常常成為
    譏刺的對象——你不妨看看少林或全真這樣的大型團隊在金庸小說中被丑化成了什么地步。

    武俠所描繪的是農(nóng)業(yè)社會的典型情況。一位高手通常只會把自己的技藝傳授給至親和少數(shù)幾個
    信得過的弟子;弟子亦然。這種結(jié)構(gòu)非常脆弱:一旦出現(xiàn)任何問題,這門技藝很容易就失傳了。

    武俠中的秘籍是這樣一種東西:你得到它以后,最好藏之名山,偷偷修煉。一旦泄漏,只會給你
    帶來殺身之禍。和師徒授受的問題相同,這樣只會讓最好的技藝在歷史長河中漸漸湮滅。現(xiàn)代社會和開放
    源代碼運動則顯示了相反的情況:知識可以由任何人獲取與學(xué)習,而不分門派貴賤。與別人分享
    知識也不會給你帶來任何壞處。

    武俠成為隱喻帶來的惡果就是,程序員以成為“高手”為榮,以炫耀技巧為樂;無視風險
    大量采用一些看上去比較炫的新技術(shù);憤世嫉俗以為天下只有自己懷才未遇;不會與人
    溝通,罔顧客戶需求,把不懂技術(shù)的用戶當白癡;不會開誠布公,總是自己偷偷留一手;
    凡此種種,不能說都是因為武俠流毒。但是在程序員中造成了不好的風氣,武俠的影響
    是不可忽視的。

    武俠是成年人的童話,但軟件開發(fā)不是童話。軟件開發(fā)要的是腳踏實地,而不是快意恩仇。
    還在做俠客夢的程序員,愿你們早點醒來。

    ?

    posted @ 2006-12-01 15:33 TreeNode 閱讀(1198) | 評論 (8)編輯 收藏

    2006年7月6日 #

    可能是為了保持平臺獨立性,SWT沒有開放許多控件的自定義接口。例如,Win32中的Button、Label、List和ComboBox都是可以自繪(Owner Draw)的,但是SWT并沒有把這些繪制方法開放出來。在最新的3.2版本中添加的一個新特性是Table和Tree現(xiàn)在支持Custom Draw了(但是并未整合到Viewer體系中),不過對于上述控件的支持仍付闕如。

    上一次,我實現(xiàn)了一個自繪的按鈕。現(xiàn)在,看到有人詢問是否可以在Combo的列表中加入圖像。其實這相當容易,只要重載Combo Widget并把自繪接口暴露出來即可。以下是簡單的代碼示例:

    package?org.eclipse.swt.widgets;

    import?java.io.*;

    import?org.eclipse.swt.SWT;
    import?org.eclipse.swt.graphics.*;
    import?org.eclipse.swt.internal.win32.*;

    public?class?CustomCombo?extends?Combo
    {
    ????
    public?CustomCombo(?Composite?parent,?int?style?)
    ????{
    ????????
    super(?parent,?style?);

    ????????
    try
    ????????{
    ????????????InputStream?is?
    =?getClass().getResourceAsStream(?"bullet.gif"?);
    ????????????image?
    =?new?Image(?getDisplay(),?is?);
    ????????????is.close();
    ????????}
    ????????
    catch?(?IOException?e?)
    ????????{
    ????????????e.printStackTrace();
    ????????}
    ????????
    final?int?CB_SETITEMHEIGHT?=?0x0153;

    ????????OS.SendMessage(?handle,?CB_SETITEMHEIGHT,?
    0,?24?);
    ????????OS.SendMessage(?handle,?CB_SETITEMHEIGHT,?
    -1,?24?);
    ????}

    ????@Override
    ????
    int?widgetStyle()
    ????{
    ????????
    final?int?CBS_OWNERDRAWFIXED?=?0x0010;
    ????????
    final?int?CBS_HASSTRINGS?=?0x0200;
    ????????
    //?final?int?CBS_OWNERDRAWVARIABLE?=?0x0020;
    ????????return?super.widgetStyle()?|?CBS_OWNERDRAWFIXED?|?CBS_HASSTRINGS;
    ????}

    ????@Override
    ????
    protected?void?checkSubclass()
    ????{
    ????}

    ????@Override
    ????
    public?void?dispose()
    ????{
    ????????image.dispose();
    ????????
    super.dispose();
    ????}

    ????
    /*?@Override
    ????LRESULT?wmMeasureChild(?int?wParam,?int?lParam?)
    ????{
    ????????MEASUREITEMSTRUCT?mis?=?new?MEASUREITEMSTRUCT();
    ????????OS.MoveMemory(?mis,?lParam,?MEASUREITEMSTRUCT.sizeof?);
    ????????mis.itemHeight?=?40;
    ????????OS.MoveMemory(?lParam,?mis,?MEASUREITEMSTRUCT.sizeof?);
    ????????return?null;?//?super.wmMeasureChild(?wParam,?lParam?);
    ????}?
    */

    ????@Override
    ????LRESULT?wmDrawChild(?
    int?wParam,?int?lParam?)
    ????{
    ????????DRAWITEMSTRUCT?dis?
    =?new?DRAWITEMSTRUCT();
    ????????OS.MoveMemory(?dis,?lParam,?DRAWITEMSTRUCT.sizeof?);

    ????????GC?gc?
    =?new?GC(?new?DCWrapper(?dis.hDC?)?);
    ????????Rectangle?rc?
    =?new?Rectangle(?dis.left,?dis.top,?dis.right?-?dis.left,
    ????????????????dis.bottom?
    -?dis.top?);
    ????????Display?display?
    =?getDisplay();
    ????????
    if?(?(dis.itemState?&?OS.ODS_SELECTED)?!=?0?)
    ????????{
    ????????????gc
    ????????????????????.setBackground(?display
    ????????????????????????????.getSystemColor(?SWT.COLOR_LIST_SELECTION?)?);
    ????????????gc.setForeground(?display
    ????????????????????.getSystemColor(?SWT.COLOR_LIST_SELECTION_TEXT?)?);
    ????????????gc.fillRectangle(?rc?);
    ????????}
    ????????
    else
    ????????{
    ????????????gc.setBackground(?display
    ????????????????????.getSystemColor(?SWT.COLOR_LIST_BACKGROUND?)?);
    ????????????gc.setForeground(?display
    ????????????????????.getSystemColor(?SWT.COLOR_LIST_FOREGROUND?)?);
    ????????????gc.fillRectangle(?rc?);
    ????????}
    ????????String?text?
    =?getItem(?dis.itemID?);
    ????????gc.drawImage(?image,?dis.left?
    +?1,?dis.top?+?1?);
    ????????gc.drawText(?text,?dis.left?
    +?20,?dis.top?);

    ????????gc.dispose();

    ????????
    return?null;
    ????}

    ????
    private?static?class?DCWrapper?implements?Drawable
    ????{
    ????????
    private?int????hdc;

    ????????DCWrapper(?
    int?hdc?)
    ????????{
    ????????????
    this.hdc?=?hdc;
    ????????}

    ????????
    public?int?internal_new_GC(?GCData?data?)
    ????????{
    ????????????
    return?hdc;
    ????????}

    ????????
    public?void?internal_dispose_GC(?int?handle,?GCData?data?)
    ????????{
    ????????}
    ????}

    ????
    private?Image????image;
    }


    值得說明的是,如果設(shè)置Combo為OwnerDraw Variable風格,則必須重載wmMeasureChild方法來指定每一項的高度。如果使用OwnerDraw Fixed風格,則只需要在構(gòu)造的時候發(fā)送一條CB_SETITEMHEIGHT消息就行了。

    ?另外一種值得考慮的選擇是將Win32的ComboBoxEx控件包裝成SWT Widget。不過,這需要轉(zhuǎn)換若干結(jié)構(gòu)并提供接口,Win32的ImageList管理機制和SWT的Image包裝方法差別比較大,使得這種方法實現(xiàn)起來麻煩的多。

    posted @ 2006-07-06 10:08 TreeNode 閱讀(2345) | 評論 (4)編輯 收藏

    2006年6月27日 #

    在上回的blog中,我抱怨過用Java用內(nèi)部類來實現(xiàn)事件回調(diào)的機制是多么難看和麻煩。在這段時間里,我一直在考慮是否有什么方法可以不用內(nèi)部類而實現(xiàn)同樣的效果。因為Java語言本身的限制,所以常規(guī)方法是行不通的。有人建議用反射——的確通過反射可以調(diào)用任意的方法,但是反射效率不佳,對頻繁發(fā)生的事件或許不太合適。動態(tài)代理也不能解決方法映射的難題。我似乎走進了死胡同。

    既然此路不通,那么C#是如何實現(xiàn)delegate的呢?過去也曾聽聞過一些內(nèi)幕,不過這次被逼才真的下決心認真去看這方面的東西。原來M$使用的是代碼生成的技術(shù):對于每個delegate,C#都會為它生成一個派生于MulticaseDelegate的對象,其中實現(xiàn)了一個和delegate簽名相同的方法。同時,對delegate的操作符+=和-=也會被編譯器處理成對MulticaseDelegate方法的調(diào)用。

    知道了這一點,接下來就需要看看Java中有沒有類似的代碼生成技術(shù)了。有意思的是,查找的時候發(fā)現(xiàn)有消息說,Java 6.0(Mutang)中將會提供動態(tài)代碼生成的功能。這的確很吸引人,不過Java6還在Beta階段,眼下還指望不上。其他比較出名的方法就是Apache becl和Objectweb ASM了。這兩個庫都比較底層,不過還有一個開源的項目——cglib——它在內(nèi)部使用了asm,不過提供了較多的實用功能。據(jù)說Hibernate和Spring都用到了這個東西。研究這個庫的時候,我一眼看到了MethodDelegate類——很明顯這就是我要找的東西了。

    MethodDelegate的設(shè)計思想很類似于C#的delegate——將接口調(diào)用轉(zhuǎn)發(fā)給類的一個成員函數(shù)。不過閱讀文檔的時候我發(fā)現(xiàn)一個問題。MethodDelegate要求其所實現(xiàn)的接口必須只有一個公共方法,但是SWT中的許多事件接口都有不止一個方法;比如,SelectionListener就有widgetSelected和idgetDefaultSelected兩個方法。因此要在SWT中使用MethodDelegate,還

    必須再多實現(xiàn)另外一層轉(zhuǎn)發(fā)。

    了解手段,接下來的事情就不難了。總結(jié)起來,需要的步驟大致如下:
    1、為每種需要實現(xiàn)的事件聲明一個接口。這是MethodDelegate的要求。
    2、用一個類實現(xiàn)SWT的事件接口,并將特定的接口調(diào)用轉(zhuǎn)發(fā)到第一步所實現(xiàn)

    的接口。
    3、用MethodDelegate提供的方法,聲明事件處理對象(Event Handler

    Target,通常為主窗體或主部件)要實現(xiàn)上述的事件接口。下面就來實現(xiàn)一下。為了簡單起見,將需要實現(xiàn)的接口聲明為事件轉(zhuǎn)發(fā)類的內(nèi)部接口,以避免維護太多接口文件(因為該接口只需要聲明一個方法,所以不會把外部類搞得太過復(fù)雜。)例如,處理部件選擇事件(widgetSelected)的類可以如下實現(xiàn):

    package org.yuhao.swt.events;

    import net.sf.cglib.reflect.MethodDelegate;

    import org.eclipse.swt.events.*;

    public class WidgetSelectedHandler implements SelectionListener
    {
    ?public WidgetSelectedHandler( Object target, String

    methodName )
    ?{
    ??delegate = (IWidgetSelectedDelegate)

    MethodDelegate.create( target,
    ????methodName,

    IWidgetSelectedDelegate.class );
    ?}

    ?public void widgetDefaultSelected( SelectionEvent e )
    ?{
    ?}

    ?public void widgetSelected( SelectionEvent e )
    ?{
    ??delegate.invoke( e );
    ?}

    ?public void invoke( SelectionEvent e )
    ?{
    ?}

    ?public interface IWidgetSelectedDelegate
    ?{
    ??void invoke( SelectionEvent e );
    ?}

    ?private IWidgetSelectedDelegate delegate;
    }

    這個接口雖然只有外部類用到,但是必須聲明為public的,否則運行會出錯(我想大概是因為代碼生成以后還是外部類,需要公開訪問權(quán)限)。為了簡化調(diào)用,再聲明一個處理事件的輔助類EventHandler,專門管理將各種事件轉(zhuǎn)發(fā)到相應(yīng)的Handler的工作:

    public class EventHandler
    {
    ?public EventHandler( Object target )
    ?{
    ??this.target = target;
    ?}

    ?public void handleSelected( Button btn, String methodName )
    ?{
    ??btn.addSelectionListener( new

    WidgetSelectedHandler( target, methodName ) );
    ?}
    ?private Object target;
    }

    這樣,在窗口中就可以簡單的如下處理事件:
    public MainShell extends Shell()
    {
    ??public MainShell( Display display )
    ?{
    ??......
    ??handler = new EventHandler( this );
    ??handler.handleSelected( btn, "btn_clicked" );
    ?}

    ?public void btn_clicked( SelectionEvent e )
    ?{
    ??...
    ?}
    }

    這里還需要注意:1、任何事件處理方法必須聲明為public的。這樣似乎有違面向?qū)ο蟮姆庋b原則,不過實際上并不會造成什么大問題。2、事件方法的簽名必須和對應(yīng)的事件方法相同。例如,widgetSelected方法有一個SelectionEvent參數(shù),那么處理該事件的btn_clicked方法也必須有且只有這一個參數(shù)。如果寫錯了,那么運行的時候會拋出異常,說找不到指定的方法。這還是需要程序員的細心來保證。還算幸運的是出錯的提示非常明顯,不必擔心使用了過度復(fù)雜的技術(shù)而找不到真正的出錯點。

    posted @ 2006-06-27 11:29 TreeNode 閱讀(1216) | 評論 (0)編輯 收藏

    2006年6月15日 #

    請比較一下這兩段功能大致相同的代碼。拋開語法的差別不論,你覺得哪種風格好?你愿意維護哪一段代碼?

    ????????b.addSelectionListener(?new?SelectionAdapter()
    ????????{
    ????????????
    public?void?widgetSelected(?SelectionEvent?e?)
    ????????????{
    ????????????????Runnable?longJob?
    =?new?Runnable()
    ????????????????{
    ????????????????????
    boolean????done????=?false;
    ????????????????????
    int????????id;

    ????????????????????
    public?void?run()
    ????????????????????{
    ????????????????????????Thread?thread?
    =?new?Thread(?new?Runnable()
    ????????????????????????{
    ????????????????????????????
    public?void?run()
    ????????????????????????????{
    ????????????????????????????????id?
    =?nextId[0]++;
    ????????????????????????????????display.syncExec(?
    new?Runnable()
    ????????????????????????????????{
    ????????????????????????????????????
    public?void?run()
    ????????????????????????????????????{
    ????????????????????????????????????????
    if?(?text.isDisposed()?)
    ????????????????????????????????????????????
    return;
    ????????????????????????????????????????text
    ????????????????????????????????????????????????.append(?
    "\nStart?long?running?task?"
    ????????????????????????????????????????????????????????
    +?id?);
    ????????????????????????????????????}
    ????????????????????????????????}?);
    ????????????????????????????????
    for?(?int?i?=?0;?i?<?100000;?i++?)
    ????????????????????????????????{
    ????????????????????????????????????
    if?(?display.isDisposed()?)
    ????????????????????????????????????????
    return;
    ????????????????????????????????????System.out
    ????????????????????????????????????????????.println(?
    "do?task?that?takes?a?long?time?in?a?separate?thread?"
    ????????????????????????????????????????????????????
    +?id?);
    ????????????????????????????????}
    ????????????????????????????????
    if?(?display.isDisposed()?)
    ????????????????????????????????????
    return;
    ????????????????????????????????display.syncExec(?
    new?Runnable()
    ????????????????????????????????{
    ????????????????????????????????????
    public?void?run()
    ????????????????????????????????????{
    ????????????????????????????????????????
    if?(?text.isDisposed()?)
    ????????????????????????????????????????????
    return;
    ????????????????????????????????????????text
    ????????????????????????????????????????????????.append(?
    "\nCompleted?long?running?task?"
    ????????????????????????????????????????????????????????
    +?id?);
    ????????????????????????????????????}
    ????????????????????????????????}?);
    ????????????????????????????????done?
    =?true;
    ????????????????????????????????display.wake();
    ????????????????????????????}
    ????????????????????????}?);
    ????????????????????????thread.start();
    ????????????????????????
    while?(?!done?&&?!shell.isDisposed()?)
    ????????????????????????{
    ????????????????????????????
    if?(?!display.readAndDispatch()?)
    ????????????????????????????????display.sleep();
    ????????????????????????}
    ????????????????????}
    ????????????????};
    ????????????????BusyIndicator.showWhile(?display,?longJob?);
    ????????????}
    ????????}?);
    另外一種:
    ????????delegate?void?NotifyStartDelegate(?int?threadId?);
    ????????
    delegate?void?NotifyFinishDelegate(?int?threadId?);
    ????????
    ????????btnInvoke.Click?
    +=?BtnInvokeClick;
    ????????
    ????????
    void?BtnInvokeClick(object?sender,?System.EventArgs?e)
    ????????{
    ????????????text.Text?
    =?"invoke?long?running?job";
    ????????????
    ????????????Cursor?
    =?Cursors.WaitCursor;
    ????????????Thread?thread?
    =?new?Thread(?new?ThreadStart(ThreadProc)?);
    ????????????thread.Start();
    ????????}
    ????????
    ????????
    private?void?ThreadProc()
    ????????{
    ????????????
    int?threadId?=?nextId?++;
    ????????????
    bool?done?=?false;
    ????????????
    ????????????
    if?(?IsDisposed?)
    ????????????????
    return;
    ????????????
    ????????????Invoke(?
    new?NotifyStartDelegate(notifyThreadStart),?new?object[]?{?threadId?}?);
    ????????????
    for?(?int?i=0;?i<100000;?i++?)
    ????????????{
    ????????????????
    if?(?IsDisposed?)
    ????????????????????
    return;
    ????????????????Console.WriteLine(?
    "do?task?that?takes?a?long?time?in?a?separate?thread?"?+?threadId?);
    ????????????}
    ????????
    ????????????
    if?(?IsDisposed?)
    ????????????????
    return;
    ????????????Invoke(?
    new?NotifyFinishDelegate(notifyThreadFinish),?new?object[]?{?threadId?}?);
    ????????????done?
    =?true;
    ????????}
    ????????
    ????????
    private?void?notifyThreadStart(?int?threadId?)
    ????????{
    ????????????text.Text?
    +=?"\r\nStart?long?task?"?+?threadId;
    ????????????threadCount?
    ++;
    ????????}
    ????????
    ????????
    private?void?notifyThreadFinish(?int?threadId?)
    ????????{
    ????????????text.Text?
    +=?"\r\nCompleted?long?running?task?"?+?threadId;????
    ????????????threadCount?
    --;
    ????????????
    if?(?threadCount?==?0?)
    ????????????????Cursor?
    =?Cursors.Default;
    ????????}
    ????????
    ????????
    private?int?nextId?=?0;
    ????????
    private?int?threadCount?=?0;

    我在另一個地方也抱怨過:在所有我了解的語言特性里面,沒有一種像Java內(nèi)部類一樣讓我覺得反感——甚至到了惡心的地步。大多作B/S系統(tǒng)的Java程 序員可能不會有這樣的感覺,因為那個領(lǐng)域基本上很少會用到這個概念。可是在C/S,不管用Swing還是SWT,內(nèi)部類都是繞不過去的一座大山。在閱讀Eclipse站點上許多代碼示例以后,我終于有了痛苦到——一點也不夸張——想要作嘔的地步。上面第一段代碼就是讓我感到窩心的代碼之一(僅僅是其中之一,還不是最丑陋的)。我想,Java 語言的發(fā)明者大概從來就沒寫過桌面程序;他根本也不打算為這個領(lǐng)域的程序員提供一種比較好的事件回調(diào)機制。

    內(nèi)部類有什么問題呢?首先,你愿意在去看代碼邏輯之前,先花上好幾分鐘去搞清楚“這個括號到底是和哪個配對”這種蠢問題嗎?你不妨回頭看看第一段的代碼,想想這段其實相當簡單的程序,是不是真的值得用這么多括號去考驗?zāi)愕闹橇Α?br />
    內(nèi)部類是對封裝的嚴重破壞。它對外部類的任何私有變量都有完全的訪問權(quán)限——如果你突然發(fā)現(xiàn)某個變量的內(nèi)容不對勁了,你不能僅僅在setXXX里面加個斷 點就指望能捕獲到錯誤;真正的元兇可能是內(nèi)部類里面的哪一句呢。如果內(nèi)部類都非常簡單,那倒也沒什么。可是當內(nèi)部類用來實現(xiàn)事件的時候,你就沒法指望它一 直那么簡單了。

    內(nèi)部類是測試的盲區(qū)。TDD總是說,要測試,測試,所有包含邏輯的類都應(yīng)當通過充分的測試。可是內(nèi)部類怎么測試?只要想想就能知道,大多數(shù)內(nèi)部類是根本沒法測試的,它和外部類實在是耦合的太緊密了。匿名內(nèi)部類的問題更嚴重——它是絕對無法測試的。你怎么測試一個連名字都沒有的方法?

    不管有意無意,內(nèi)部類在(至少我看到的)實踐中事實上鼓勵了不好的編程風格。就是說,它違背了DRY(Don't Repeat Yourself)的原則。比如,button.addSelectionListener后面幾乎總是跟著SelectionAdapter+括號+ widgetSelected原型再+一堆括號;Display.asyncExec后面總是要寫上new Runnble(),void run(),括號,等等。千篇一律的東西,可是又不得不寫。而且,幾乎沒有什么好的辦法可以改進!因為語法的規(guī)則要求你必須這樣做。每天寫這些無聊的東 西,你的話會不會煩?哦,工具是有的。可是工具只負責生成代碼,以后的維護還是要你來做——不是么?
    posted @ 2006-06-15 22:37 TreeNode 閱讀(3597) | 評論 (31)編輯 收藏

    2006年6月3日 #

         摘要: 本文試圖透過SWT面向?qū)ο蟮姆庋b,深入研究界面框架的構(gòu)造中最復(fù)雜也可以說是最有趣的問題之一——即界面框架如何以面向?qū)ο蟮姆绞綄崿F(xiàn)操作系統(tǒng)底層的消息機制。  閱讀全文
    posted @ 2006-06-03 12:46 TreeNode 閱讀(3480) | 評論 (10)編輯 收藏

    僅列出標題  
    主站蜘蛛池模板: 亚洲AV无码一区二区三区在线观看| 国内精品免费在线观看| 亚洲日韩乱码中文字幕| 亚洲精品美女在线观看| 久久久无码精品亚洲日韩京东传媒 | 亚洲专区先锋影音| 亚洲天天做日日做天天欢毛片| 亚洲人成电影福利在线播放| 久久国产亚洲电影天堂| 亚洲视频在线观看一区| 亚洲色图黄色小说| 亚洲免费人成视频观看| 亚洲深深色噜噜狠狠网站| 学生妹亚洲一区二区| 亚洲国产精品美女久久久久| 在线亚洲v日韩v| 国产黄在线播放免费观看| 鲁丝片一区二区三区免费| 久久国产精品成人片免费| 免费阿v网站在线观看g| 爽爽日本在线视频免费| 天堂亚洲免费视频| 亚洲狠狠婷婷综合久久久久| 亚洲综合在线视频| 456亚洲人成影院在线观| 综合偷自拍亚洲乱中文字幕| 一级a性色生活片久久无少妇一级婬片免费放| 一道本不卡免费视频| 野花香高清在线观看视频播放免费| 69视频在线是免费观看| 成人毛片18女人毛片免费96| 亚洲成a人一区二区三区| 国精无码欧精品亚洲一区| 久久久久亚洲精品日久生情 | 亚洲精品色婷婷在线影院| 亚洲AV无码专区国产乱码电影| 亚洲毛片基地日韩毛片基地| 亚洲另类无码专区丝袜| 国产日韩久久免费影院 | 久久99久久成人免费播放| 99精品视频在线观看免费播放|