Android開發,別把Java當Javascript

林堯彬發表於2020-04-04

   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的風格,那樣你會很難維護。

轉載於:https://www.cnblogs.com/liyunfan/p/3499404.html

相關文章