請比較一下這兩段功能大致相同的代碼。拋開語法的差別不論,你覺得哪種風(fēng)格好?你愿意維護(hù)哪一段代碼?
????????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)機(jī)制。
內(nèi)部類有什么問題呢?首先,你愿意在去看代碼邏輯之前,先花上好幾分鐘去搞清楚“這個括號到底是和哪個配對”這種蠢問題嗎?你不妨回頭看看第一段的代碼,想想這段其實相當(dāng)簡單的程序,是不是真的值得用這么多括號去考驗?zāi)愕闹橇Α?br />
內(nèi)部類是對封裝的嚴(yán)重破壞。它對外部類的任何私有變量都有完全的訪問權(quán)限——如果你突然發(fā)現(xiàn)某個變量的內(nèi)容不對勁了,你不能僅僅在setXXX里面加個斷
點就指望能捕獲到錯誤;真正的元兇可能是內(nèi)部類里面的哪一句呢。如果內(nèi)部類都非常簡單,那倒也沒什么。可是當(dāng)內(nèi)部類用來實現(xiàn)事件的時候,你就沒法指望它一
直那么簡單了。
內(nèi)部類是測試的盲區(qū)。TDD總是說,要測試,測試,所有包含邏輯的類都應(yīng)當(dāng)通過充分的測試。可是內(nèi)部類怎么測試?只要想想就能知道,大多數(shù)內(nèi)部類是根本沒法測試的,它和外部類實在是耦合的太緊密了。匿名內(nèi)部類的問題更嚴(yán)重——它是絕對無法測試的。你怎么測試一個連名字都沒有的方法?
不管有意無意,內(nèi)部類在(至少我看到的)實踐中事實上鼓勵了不好的編程風(fēng)格。就是說,它違背了DRY(Don't Repeat
Yourself)的原則。比如,button.addSelectionListener后面幾乎總是跟著SelectionAdapter+括號+
widgetSelected原型再+一堆括號;Display.asyncExec后面總是要寫上new Runnble(),void
run(),括號,等等。千篇一律的東西,可是又不得不寫。而且,幾乎沒有什么好的辦法可以改進(jìn)!因為語法的規(guī)則要求你必須這樣做。每天寫這些無聊的東
西,你的話會不會煩?哦,工具是有的。可是工具只負(fù)責(zé)生成代碼,以后的維護(hù)還是要你來做——不是么?