検索

ホームページ  >  に質問  >  本文

设计模式 - 为什么很多人写 Java/Android 时,选择让同一个类实现多个接口,而不是用多个内部匿名类?

呃…… 标题不太好。让我在问题描述里解释一下。

让我以 Android 开发中一个简单的例子说明:在一个 Activity 中有多个可点击的按钮时,很多人会这么写:

public class ExampleActivity extends Activity implements OnClickListener {

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_example);

        findViewById(R.id.first_button).setOnClickListener(this);
        findViewById(R.id.second_button).setOnClickListener(this);
    }

    @Override
    public void onClick(final View v) {
        switch (v.getId()) {
            case R.id.first_button:
                // bla bla bla
                break;
            case R.id.second_button:
                // bra bra bra
        }
    }
    
}

事实上,Android 官方有些 sample 里面也是这么写的。然而在我看来,这么写代码是非常不优雅的,因为一个 OnClickListener 的实现,只应该关注和点击事件本身相关的内容,它的含义和 Activity 的含义是截然无关的,让同一个类继承/实现他们,会使得这个类的含义变得不清晰。同时,这样还给 ExampleActivity 类增加了一个 public 的方法,削弱了这个类的封闭性。

所以如果像下面这样,会好不少:

public class ExampleActivity extends Activity {

    private OnClickListener onClickListener = new OnClickListener() {
        @Override
        public void onClick(final View v) {
            switch (v.getId()) {
                case R.id.first_button:
                    // bla bla bla
                    break;
                case R.id.second_button:
                    // bra bra bra
            }
        }
    };

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_example);

        findViewById(R.id.first_button).setOnClickListener(onClickListener);
        findViewById(R.id.second_button).setOnClickListener(onClickListener);
    }
    
}

这样写体现了 composition over inheritance 的思想。它避免了上面的所有问题,看起来舒服得多。

不过,这样还是让阅读代码时很不方便——看到 onCreate 里面时,还不得不经常滚动到声明 onClickListener 的地方去,并且在 onClick 中艰难的寻找真正和某个特定按钮相关的代码。当然这两个问题之前那个版本也都无法避免。

另一件糟糕的事情是,不同按钮的 listener 逻辑很可能是相对独立的,放到同一个 onClickListener 里,还是很丑陋。

所以为了进一步避免这几个问题,我一向都是用下面这样的写法:

public class ExampleActivity extends Activity {

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_example);

        findViewById(R.id.first_button).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(final View v) {
                // bla bla bla
            }
        });
        findViewById(R.id.second_button).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(final View v) {
                // bra bra bra
            }
        });
    }
    
}

这样的话,不同逻辑间相对独立,看起来非常舒服,便于阅读,并且让我找回了写 JavaScript 的舒畅感觉(划掉)。

那么问题来了:为什么有的人不使用最后一种写法呢,倒反是使用第一种写法呢?

高洛峰高洛峰2888日前390

全員に返信(9)返信します

  • 巴扎黑

    巴扎黑2017-04-17 18:02:20

    私も 2 番目の書き方に同意します。その利点についてはすでに述べたので、詳細は説明しません。しかし、私はいつもなぜ Google の公式サンプルが最初の方法で書かれているのか疑問に思っていましたが、後で Android ドキュメントの次のような一文を目にしました。その内容は次のとおりです。Java でオブジェクトを作成すると、実際には非常にコストがかかります。プログラムが大量の追加オーバーヘッドを生成する原因となります。 Google がすべての監視を完了するために同じオブジェクトを使用することを選択したのは、主に初期の Android デバイスのパフォーマンスがそれほど優れておらず、そのような詳細についてのみパフォーマンスを節約できたためです。しかし、現在では、デバイスはあまり気にする必要がなくなりました。これらについては、2 番目の方法を使用しても問題はありません。

    返事
    0
  • PHP中文网

    PHP中文网2017-04-17 18:02:20

    個人的には最後の書き方の方が好みなので、OnClickListenerでやることがたくさんある場合は別途名前付きクラスとして書くかもしれません。

    しかし、実際には最初の書き方がたくさんあるのですが、その理由の 1 つは、例が誤解を招きやすいということだと思います。通常、例のコードは比較的小さいため、この方法で書いても問題はありません。非常に長いですが、実際のビジネスでは、コードはさらに長くなる可能性があります。もちろん、クラスを書くことがメソッドを書くことよりも重いと考える人もいるかもしれません。理論的かつ実践的な根拠については分析を行っている人もいますので、これ以上は述べません。

    C# では、さまざまなボタンの Click イベントを実装するためにデリゲートが使用され、各デリゲートは現在のフォームの特定の XxxxButton_Click メソッドに対応します (WinForm プログラムの場合、ASP.NET アプリケーションも同様です)。書き方 もちろん、UI Designer がこれをやりたいという事実は排除できません。 Lambda の人気により、元の投稿者と同様の 3 番目の記述方法を使用する人が増えていますが、これはインターフェイスではなく、単なる Lambda です。また、UI デザイナーを使用せずに生成するという前提があります。コードの状況。

    なぜ C# について話すのかというと、Java は Lambda の優位性を認識しており、Lambda も実装し、単一のメソッド インターフェイスの形式で C# の委任をシミュレートしているため、3 番目の記述方法はより簡単になります。

    しかし、Lambda には便利な面もありますが、特にロジックが複雑な場合には不便な面もあります。したがって、より複雑なロジックを使用したイベント処理の場合は、イベント インターフェイスの実装 (OnClickListener の実装など)、または独立した (または関連する一連の) ビジネス処理クラスである別の処理クラスを作成することをお勧めします。 . onClick で 1 つの文で呼び出します (new Business(params).Go() など)

    返事
    0
  • 怪我咯

    怪我咯2017-04-17 18:02:20

    継承よりも構成
    これをどう理解しますか?合成は継承よりも優れているのでしょうか?相続に対する悪評のせいで、今では誰もが相続を見たら先手を打って拒否するようになりました。
    しかし、これは本当にそうなのでしょうか?私は同意しません。
    OO におけるオブジェクトの関係は、関連、依存、結合、集約、継承、一般化などのいくつかの関係に大別され、これらの関係によってオブジェクト間の関係が規定されます。
    つまり、オブジェクト間の関係は、正確に言うと、継承の概念により結合がまったく存在しないため、実装が簡単なのでわかりません。再利用のために継承を使用する 実際、上記のことはオブジェクトの抽象化が不十分であることが原因です。
    そこで、みんなの考えを単純化するために、継承よりも結合の方が優れているという考えを単純に提唱する人もいます。

    本題に戻りますが、なぜ Google はこの方法を実装するのでしょうか?ビジネスの観点から見ると、アクティビティのライフ サイクルはいくつかの段階に分かれます。ただし、オブジェクトのライフ サイクル全体が、テンプレートを通じて公開されるのは非常に合理的な設計です。質問者さんはそのような考えをお持ちでしょう。

    返事
    0
  • 巴扎黑

    巴扎黑2017-04-17 18:02:20

    これは、外国人がプログラムのオーバーヘッドについて深く理解していることです。骨髄まで深く浸透してしまいました。

    返事
    0
  • 巴扎黑

    巴扎黑2017-04-17 18:02:20

    Android を学び始めた当初は常に 2 番目の書き方を使用していましたが、リスナーのコンテンツが多い場合はあまり見栄えがよくありません。

    返事
    0
  • 怪我咯

    怪我咯2017-04-17 18:02:20

    リスナーが非常に少ない場合にのみ匿名クラスを作成し、通常、クリック イベント コードが大きい場合は、別のメソッドを作成して onClick で実行します。このコードロジックはより明確で整然としたものになると思います。

    返事
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-17 18:02:20

    イベント処理をエントリプロセスに抽象化し、それを共通インターフェースに抽象化します。これはクラスの概念であり、ビジネスに固有のものではありません。
    後で記述する方法は、明らかにオブジェクトの概念に特有です。

    私もこのように書くことに慣れてきました。とにかく、特定のビジネスと特定のオブジェクトであり、一般的である必要はありません。

    返事
    0
  • ringa_lee

    ringa_lee2017-04-17 18:02:20

    一度使用したオブジェクトは匿名で使用してください。

    返事
    0
  • 天蓬老师

    天蓬老师2017-04-17 18:02:20

    内部クラスが外部クラスのインスタンスを保持するだけで、メモリ リークが簡単に発生する可能性があります
    パフォーマンスの点では、最初の書き方の方が 2 番目の書き方よりも優れています。 3 番目の書き方よりも優れた書き方

    返事
    0
  • キャンセル返事