Android開發中,匿名類的應用使得開發更加靈活,而匿名類的濫用卻讓程序變得雜亂無章、難以維護。程序員都是喜歡偷懶的,我們常常看到一個Activity類中布滿了匿名類,有Thread、有Handler、有Adapter、有各種Listener以及給匿名類傳遞數據的final變量,給人的感覺就像隨地大小便,很不文明。
Java畢竟不是Javascript,沒有閉包和沒有匿名函數,把面向對象設計的語言用函數式編程的思想來寫是很牽強的。下面貼一段JS代碼:
MongoClient.connect('mongodb://127.0.0.1:27017/test', function(err, db) {
if(err) throw err;
db.dropDatabase(function(err, done) {
db.createCollection('test_custom_key', function(err, collection) {
collection.insert({'a':1}, function(err, docs) {
collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}).toArray(function(err, items) {
console.dir(items);
// Let's close the db
db.close();
});
});
});
});
});
得益於JS的閉包、匿名函數、函數即變量等特性,JS開發者可以將函數嵌套發揮到令人嘆為觀止的地步,而且即使套了很多層,也不會顯得很臃腫。你能想想如果Java套這么多層會是什么效果嗎?或許編輯器都顯示不下最長的縮進了,但能多Android開發者就好這口,來看個例子:
public class MyActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ((ListView) findViewById(R.id.listview)).setAdapter(new BaseAdapter() { @Override public View getView(int arg0, View arg1, ViewGroup arg2) { if (arg1 == null) { arg1 = LayoutInflater.from(MyActivity.this).inflate( R.id.feed, arg2, false); } arg1.findViewById(R.id.subject).setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { final Handler handler = new Handler( new Callback() { @Override public boolean handleMessage( Message msg) { // 此處省略50行 return false; } }); new Thread(new Runnable() { @Override public void run() { // 此處省略100行 handler.sendEmptyMessage(0); } }).start(); } }); return arg1; } // 。。。。。。 }); } }
這是個真實的例子,其中很多習慣很多安卓開發者也有。這個onCreate函數竟然有近千行代碼,已經完全喪失可讀性了。如果你走了有人來接替你的活兒,看到這樣的代碼他一定會把你罵死。
下面我就Android開發中常見的匿名類給出自己的替代方案,僅供參考,錯誤之處還請指證。
用繼承接口代替匿名類
這種方式適合各種Listener,這里以OnClickListener為例作說明。綁定事件一般是在onCreate時完成的,所以在onCreate中可能會有很多個OnClickListener的匿名類,比如:
findViewById(R.id.button).setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { // .... } });
這時onCreate方法就會變得冗長,而且包含很多不應該出現在onCreate中的處理邏輯,如果我們讓Activity去實現OnClickListener,將所有點擊處理的listener都設為這個activity,在onClick中再根據view.getId()來分支處理,程序的結構就會緊湊很多:
public class DummyActivity extends Activity implements OnClickListener { //..... @Override protected void onCreate(Bundle savedInstanceState) { for(int id : in new int[] { R.id.button }) { findViewById(id).setOnClickListener(this); } } @Override public void onClick(View arg0) { switch(arg0.getId()) { case R.id.button: // ...... break; } } }
包括onLongClickListener等針對單一View的Listener都可以通過讓Activity繼承接口的方式來操作。如果是針對列表項的ItemListener,建議讓Adapter去繼承接口。
用子類代替匿名類
如果一個Activity中有AdapterView的子類,如ListView、GridView,就會設置相應的Adapter,除了最簡單的情形可以用SimpleAdapter、ArrayAdapter直接映射,很多情況都都需要繼承BaseAdapter之類后重寫一些方法,邏輯比較簡單時用內部類不失為可取辦法。例如很多人喜歡在onCreate中這么寫:
((ListView)findViewById(R.id.listView)).setAdapter(new BaseAdapter() { @Override public View getView(int arg0, View arg1, ViewGroup arg2) { //...... } //...... });
為了不在onCreate參雜不相關的邏輯,保持代碼結構合理,你可以嘗試這樣處理:
public class DummyActivity extends Activity { //..... @Override protected void onCreate(Bundle savedInstanceState) { ((ListView)findViewById(R.id.listView)).setAdapter(new MyAdapter(....)); } private static class MyAdapter extends Base Adapter{ MyAdapter(...) { ....... } @Override public View getView(int arg0, View arg1, ViewGroup arg2) { ......... } } }
至於static與否則取決於你是否要用到動態成員變量了,這種方式使得參數傳遞也更加方便:只需通過構造函數傳入,不必通過final關鍵字。
用外部類代替匿名類
對於Adapter這類邏輯簡單的類可以用內部類處理,如果涉及到更加復雜的邏輯,比如數據處理、I/O、數據庫操作等,建議用外部類使各個類的業務屬性盡可能單一。比如Thread類,跟Activity類相關度本來就很低,為了偷懶非得硬生生塞進Activity中顯然是不合適的,無論是匿名類還是子類的形式,這里就不舉具體例子了。至於外部類的命名,如果想不到合適的名字,可考慮前綴命名,比如現在有一個DummyActivity,之前在里面塞了一個MyThread,那提出來再叫MyThread顯然不合適,就以前綴命名的方式改為DummyActivityThread就一目了然了。
小結
Javascript是門很優秀的語言,擁有函數式編程的諸多特性,合理的靈活嵌套可以使代碼緊湊而優雅,而Java是一門嚴格面向對象的語言,匿名類嵌套的濫用會讓你的程序臃腫混亂,建議不要把Java寫成了Javascript的風格,那樣你會很難維護。
