请比较一下这两段功能大致相同的代码。抛开语法的差别不论,你觉得哪种风格好?你愿意维护哪一段代码?
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内部类一样让我觉得反感——甚至到了恶心的地步。大多作B/S系统的Java程
序员可能不会有这样的感觉,因为那个领域基本上很少会用到这个概念。可是在C/S,不管用Swing还是SWT,内部类都是绕不过去的一座大山。在阅读Eclipse站点上许多代码示例以后,我终于有了痛苦到——一点也不夸张——想要作呕的地步。上面第一段代码就是让我感到窝心的代码之一(仅仅是其中之一,还不是最丑陋的)。我想,Java
语言的发明者大概从来就没写过桌面程序;他根本也不打算为这个领域的程序员提供一种比较好的事件回调机制。
内部类有什么问题呢?首先,你愿意在去看代码逻辑之前,先花上好几分钟去搞清楚“这个括号到底是和哪个配对”这种蠢问题吗?你不妨回头看看第一段的代码,想想这段其实相当简单的程序,是不是真的值得用这么多括号去考验你的智力。
内部类是对封装的严重破坏。它对外部类的任何私有变量都有完全的访问权限——如果你突然发现某个变量的内容不对劲了,你不能仅仅在setXXX里面加个断
点就指望能捕获到错误;真正的元凶可能是内部类里面的哪一句呢。如果内部类都非常简单,那倒也没什么。可是当内部类用来实现事件的时候,你就没法指望它一
直那么简单了。
内部类是测试的盲区。TDD总是说,要测试,测试,所有包含逻辑的类都应当通过充分的测试。可是内部类怎么测试?只要想想就能知道,大多数内部类是根本没法测试的,它和外部类实在是耦合的太紧密了。匿名内部类的问题更严重——它是绝对无法测试的。你怎么测试一个连名字都没有的方法?
不管有意无意,内部类在(至少我看到的)实践中事实上鼓励了不好的编程风格。就是说,它违背了DRY(Don't Repeat
Yourself)的原则。比如,button.addSelectionListener后面几乎总是跟着SelectionAdapter+括号+
widgetSelected原型再+一堆括号;Display.asyncExec后面总是要写上new Runnble(),void
run(),括号,等等。千篇一律的东西,可是又不得不写。而且,几乎没有什么好的办法可以改进!因为语法的规则要求你必须这样做。每天写这些无聊的东
西,你的话会不会烦?哦,工具是有的。可是工具只负责生成代码,以后的维护还是要你来做——不是么?