請(qǐng)比較一下這兩段功能大致相同的代碼。拋開語(yǔ)法的差別不論,你覺得哪種風(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;
我在另一個(gè)地方也抱怨過(guò):在所有我了解的語(yǔ)言特性里面,沒有一種像Java內(nèi)部類一樣讓我覺得反感——甚至到了惡心的地步。大多作B/S系統(tǒng)的Java程
序員可能不會(huì)有這樣的感覺,因?yàn)槟莻€(gè)領(lǐng)域基本上很少會(huì)用到這個(gè)概念。可是在C/S,不管用Swing還是SWT,內(nèi)部類都是繞不過(guò)去的一座大山。在閱讀Eclipse站點(diǎn)上許多代碼示例以后,我終于有了痛苦到——一點(diǎn)也不夸張——想要作嘔的地步。上面第一段代碼就是讓我感到窩心的代碼之一(僅僅是其中之一,還不是最丑陋的)。我想,Java
語(yǔ)言的發(fā)明者大概從來(lái)就沒寫過(guò)桌面程序;他根本也不打算為這個(gè)領(lǐng)域的程序員提供一種比較好的事件回調(diào)機(jī)制。
內(nèi)部類有什么問題呢?首先,你愿意在去看代碼邏輯之前,先花上好幾分鐘去搞清楚“這個(gè)括號(hào)到底是和哪個(gè)配對(duì)”這種蠢問題嗎?你不妨回頭看看第一段的代碼,想想這段其實(shí)相當(dāng)簡(jiǎn)單的程序,是不是真的值得用這么多括號(hào)去考驗(yàn)?zāi)愕闹橇Α?br />
內(nèi)部類是對(duì)封裝的嚴(yán)重破壞。它對(duì)外部類的任何私有變量都有完全的訪問權(quán)限——如果你突然發(fā)現(xiàn)某個(gè)變量的內(nèi)容不對(duì)勁了,你不能僅僅在setXXX里面加個(gè)斷
點(diǎn)就指望能捕獲到錯(cuò)誤;真正的元兇可能是內(nèi)部類里面的哪一句呢。如果內(nèi)部類都非常簡(jiǎn)單,那倒也沒什么??墒钱?dāng)內(nèi)部類用來(lái)實(shí)現(xiàn)事件的時(shí)候,你就沒法指望它一
直那么簡(jiǎn)單了。
內(nèi)部類是測(cè)試的盲區(qū)。TDD總是說(shuō),要測(cè)試,測(cè)試,所有包含邏輯的類都應(yīng)當(dāng)通過(guò)充分的測(cè)試??墒莾?nèi)部類怎么測(cè)試?只要想想就能知道,大多數(shù)內(nèi)部類是根本沒法測(cè)試的,它和外部類實(shí)在是耦合的太緊密了。匿名內(nèi)部類的問題更嚴(yán)重——它是絕對(duì)無(wú)法測(cè)試的。你怎么測(cè)試一個(gè)連名字都沒有的方法?
不管有意無(wú)意,內(nèi)部類在(至少我看到的)實(shí)踐中事實(shí)上鼓勵(lì)了不好的編程風(fēng)格。就是說(shuō),它違背了DRY(Don't Repeat
Yourself)的原則。比如,button.addSelectionListener后面幾乎總是跟著SelectionAdapter+括號(hào)+
widgetSelected原型再+一堆括號(hào);Display.asyncExec后面總是要寫上new Runnble(),void
run(),括號(hào),等等。千篇一律的東西,可是又不得不寫。而且,幾乎沒有什么好的辦法可以改進(jìn)!因?yàn)檎Z(yǔ)法的規(guī)則要求你必須這樣做。每天寫這些無(wú)聊的東
西,你的話會(huì)不會(huì)煩?哦,工具是有的??墒枪ぞ咧回?fù)責(zé)生成代碼,以后的維護(hù)還是要你來(lái)做——不是么?