이전 글 Kotlin abstract class vs interface 특징에서 추상 클래스와 인터페이스 각각의 특징을 살펴보았다. 이번 글은 그 특징들에 기반한 각각의 사용목적에 대해 알아보는 글이다.
결론부터 말하자면,
추상 클래스는 하위 클래스에서 상위 클래스의 공통 기능을 사용하거나 추가 확장하기 위해서 사용한다.
인터페이스는 클래스가 특정 기능을 구현하도록 강제하여, 동일한 인터페이스를 구현한 객체들에 대해 일관된 동작을 보장하기 위해서 사용한다.
안드로이드에서는 어떤 예시가 있는지 살펴보겠다. 예시 코드는 이해를 돕기 위해 많이 간소화시킨 코드로 실제 코드와는 다르다.
class FirstFragment: Fragment() {
fun bindView() {
// View Binding Code
}
}
class SecondFragment: Fragment() {
fun bindView() {
// View Binding Code
}
}
예를 들어 2개의 프래그먼트가 있고 비슷한 동작을 하는 메서드가 있다고 가정하자. 2개일 때는 이렇게 해도 상관없어 보이지만, 프래그먼트의 수가 점점 많아진다면 상위 클래스의 공통 메서드를 하나 만드는 게 낫지 않을까?
모든 프래그먼트에 ViewBinding을 사용할 거라면, abstract class BaseFragment 상위 클래스를 만들어서 공통 메서드를 선언해주는 것이 바람직하다.
abstract class BaseFragment: Fragment() {
abstract fun bindView()
}
class FirstFragment: BaseFragment() {
override fun bindView() {
// View Binding Code
}
}
class SecondFragment: BaseFragment() {
override fun bindView() {
// View Binding Code
}
}
위의 프로젝트에서 CustomButton 클래스가 추가된다고 가정해보자. 그리고 SecondFragment에 버튼이 존재하고 클릭했을 때의 이벤트 메서드도 필요하다고 가정해보자.
//...
class SecondFragment: BaseFragment() {
override fun bindView() {
// View Binding Code
}
fun onClickButton() {
// Clicked Button Code
}
}
class CustomButton(context: Context): Button(context) {
fun onClickButton() {
// Clicked Button Code
}
}
이런 경우에 onClickButton() 메서드를 BaseFragment에 선언해서 해결하기는 곤란하다. FirstFragment에는 버튼을 클릭하는 동작이 전혀 필요없고, Fragment와 Button은 다른 성질의 클래스이기 때문에 하나로 묶기가 애매해진다. 이럴 때 필요한 것이 바로 인터페이스이다.
interface OnButtonClickListener {
fun onButtonClick()
}
class SecondFragment: BaseFragment(), OnButtonClickListener {
override fun bindView() {
// View Binding Code
}
override fun onButtonClick() {
// Clicked Button Code
}
}
class CustomButton(context: Context): Button(context), OnButtonClickListener {
override fun onButtonClick() {
// Clicked Button Code
}
}
SecondFragment와 CustomButton은 다른 성질을 가진 클래스이지만 OnButtonClickListener라는 인터페이스를 구현함으로서 클래스가 버튼을 클릭하는 기능을 구현하도록 강제하여, 버튼을 클릭한다는 일관된 동작을 보장한다. 인터페이스는 다중 구현도 가능하기 때문에 얼마든지 다른 인터페이스를 구현 클래스에 추가할 수 있다는 점도 장점이다.