찾다

 >  Q&A  >  본문

设计模式 - 为什么很多人写 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일 전385

모든 응답(9)나는 대답할 것이다

  • 巴扎黑

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

    저도 두 번째 글쓰기 방식에 동의합니다. 이미 장점을 말씀하셨으니 자세한 내용은 다루지 않겠습니다. 그러나 나는 Google의 공식 예제가 왜 첫 번째 방식으로 작성되었는지 항상 궁금했습니다. 나중에 Android 문서에서 대략 다음과 같은 단락을 보았습니다. Java에서 객체를 생성하는 것은 실제로 매우 비쌉니다. 프로그램이 많은 추가 오버헤드를 생성하게 만듭니다. 그렇기 때문에 Google은 모든 모니터링을 완료하기 위해 동일한 개체를 사용하기로 결정했습니다. 이는 주로 초기 Android 기기의 성능이 그다지 좋지 않았고 이러한 세부 사항에만 성능을 저장할 수 있었기 때문입니다. 이제 기기는 더 이상 크게 신경 쓸 필요가 없습니다. 따라서 두 번째 방법을 사용하는 것도 무해합니다.

    회신하다
    0
  • PHP中文网

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

    저는 개인적으로 마지막 방식을 선호하는데 OnClickListener가 할 일이 많으면 별도로 네임드 클래스로 작성해도 될 것 같습니다.

    그러나 실제로 많은 첫 번째 작성 방법이 있는 것 같습니다. 한 가지 이유는 예제 코드가 일반적으로 상대적으로 작기 때문에 이러한 방식으로 작성하는 데 문제가 없으며 코드가 오해의 소지가 있기 때문입니다. 매우 길지만 실제 비즈니스에서는 코드가 훨씬 더 많을 수 있습니다. 또 다른 이유는 일부 사람들이 클래스 작성이 너무 무겁다고 생각하기 때문일 수 있습니다. 물론 실제로 클래스 작성이 방법 작성보다 더 무겁지는 않습니다. 이론적, 실무적 근거를 가지고 분석을 하신 분들도 계시기 때문에 더 이상 말씀드리지 않겠습니다.

    C#에서 대리자는 다양한 버튼의 Click 이벤트를 구현하는 데 사용되며 각 대리자는 현재 Form의 특정 XxxxButton_Click 메서드에 해당합니다(WinForm 프로그램인 경우 ASP.NET 응용 프로그램도 유사합니다). 작성 방식은 고대 VB/VC에서 계승되었습니다. 물론 UI Designer가 이를 원한다는 사실도 배제할 수 없습니다. Lambda의 인기로 인해 점점 더 많은 사람들이 원본 포스터와 유사한 세 번째 작성 방법을 사용하기 시작했지만 이는 인터페이스가 아니라 단지 Lambda일 뿐이며 UI Designer를 사용하여 생성하지 않고 전제가 있습니다. 코드 상황.

    C#이라고 말하는 이유는 Java가 Lambda의 우수성을 깨달았기 때문에 Lambda도 구현하여 단일 메소드 인터페이스 형태로 C#의 위임을 시뮬레이션했기 때문에 세 번째 작성 방법이 더 간단할 수 있기 때문입니다.

    그러나 모든 것에는 양면이 있습니다. Lambda는 편리함을 제공하지만 불편함도 가져옵니다. 특히 로직이 복잡할 경우 더욱 그렇습니다. 따라서 보다 복잡한 논리를 사용하는 이벤트 처리의 경우 이벤트 인터페이스 구현(예: OnClickListener 구현) 또는 독립적(또는 관련 묶음) 비즈니스 처리 클래스일 수 있는 별도의 처리 클래스를 작성하는 것이 좋습니다. . onClick에서 한 문장으로 호출하세요(예: new Business(params).Go())

    회신하다
    0
  • 怪我咯

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

    상속보다 구성
    이것을 어떻게 이해하시나요? 구성이 상속보다 나은가요? 상속에 대한 나쁜 평판 때문에 이제는 모든 사람이 상속을 보면 선제적으로 거부합니다.
    그런데 정말 그럴까요? 나는 동의하지 않습니다.
    OO의 객체 관계는 크게 연관, 의존, 조합, 집합, 상속, 일반화 등 여러 관계로 구분됩니다.
    즉, 객체 간의 관계는 정확히 말하면 상속이라는 개념으로 인해 결합이 전혀 없다는 것입니다. 상속은 구현하기 쉽기 때문에 누구나 좋아합니다. 재사용을 위해 상속을 사용하십시오. 실제로 위의 내용은 객체 추상화가 부족하여 발생합니다.
    그래서 어떤 사람들은 모든 사람의 생각을 단순화하기 위해 상속보다 조합이 더 낫다는 생각을 단순히 내세웁니다.

    본문으로 돌아가서, Google은 왜 이런 식으로 구현합니까? 비즈니스 관점에서 보면 액티비티의 라이프사이클은 여러 단계로 이루어지는데, 전체 객체 라이프사이클은 Android에서 구현됩니다. 그러나 템플릿을 통해 몇 가지 핵심 사항을 노출하는 것은 매우 합리적인 설계입니다. 질문자는 그런 생각을 갖고 있을 겁니다.

    회신하다
    0
  • 巴扎黑

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

    이것은 프로그램 오버헤드에 대한 외국인들의 깊은 이해입니다. 골수 깊숙이 침투했습니다.

    회신하다
    0
  • 巴扎黑

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

    저는 항상 두 번째 방법을 써왔는데 안드로이드를 처음 배울 때는 세 번째 방법이었는데 듣는 사람이 내용이 많으면 별로 안 좋아보이네요

    회신하다
    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

    내부 클래스가 외부 클래스의 인스턴스를 보유하기 때문에 쉽게 메모리 누수가 발생할 수 있습니다
    성능 측면에서는 첫 번째 작성 방법이 두 번째 작성 방법보다 낫고 두 번째 방법이 더 좋습니다. 세 번째 글쓰기 방식보다 글쓰기가 더 좋습니다

    회신하다
    0
  • 취소회신하다