Why your “BaseActivity” is anti-pattern. Kotlin Delegations

Zorbey Torunoğlu
3 min readMay 5, 2024

--

Often, you can see people having classes named “BaseActivity” or “BaseFragment”. The main argument of theirs is they sometimes need to override some behaviours in all of their activity classes, and then inheritence seems a good choice.

What is the problem using inheritence? Why it is anti-pattern?

“BaseFragment, BaseActivity, BaseX” naming pattern is completely anti-pattern to begin with. Names should be clear about their responsbilities.

The reason why people can’t name those parent classes correctly is that they find themselves in a situation where they need a single parent class yet multiple responsibilities.

For example, let’s say you want to log when user leaves the screen and log once again when they get back and you want this process for all of your activities. The most common approach would be having a parent activity, let’s name it “AnalitycsActivity” for example.

Later on, let’s say you have an incoming data from somewhere and you need to override “onNewIntent” on some or all of your activities. Let’s call it “IntentHandlerActivity”.

As you can see, now we have two parent activity classes, one AnalitycsActivity, one IntentHandlerActivity.

Many developers would go and create a single “BaseActivity” and implement all those different responsibilities in a single class when they face such an issue.

That’s totally anti-pattern.

Kotlin Delegations

Delegation is a common pattern that is used in any programming language. Yet, our advantage here is that Kotlin supports delegations natively.

It is a powerful feature that allows you to delegate functionality from one object to another. It enables you to reuse code and compose objects in a flexible and concise manner.

  1. Interfaces: Kotlin delegation is commonly used with interfaces. Instead of implementing all methods of an interface in a class, you can delegate the implementation to another object.
  2. Delegated Properties: Kotlin also supports delegation for properties. You can use delegated properties to define custom behavior for property access and modification without explicitly implementing getter and setter methods.

It is quite likely that you already witnessed its power if you ever saw lines like “by lazy” or “by viewModels()”.

How Kotlin Delegation will fix our problem?

Let’s go step by step. We will need an interface and an implementation of our interface.

interface AnalyticsLogger {
fun registerLifecycleOwner(owner: LifecycleOwner)
}

Our implementation class should also implement LifecycleEventObserver for us to be able to observe the state changes in our registered lifecycle owner.

class AnalyticsLoggerImpl : AnalyticsLogger, LifecycleEventObserver {
override fun registerLifecycleOwner(owner: LifecycleOwner) {
owner.lifecycle.addObserver(this)
}

override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
when (event) {
Lifecycle.Event.ON_START -> println("User opened the screen.")
Lifecycle.Event.ON_PAUSE -> println("User leaved the screen.")
else -> Unit
}
}

}

Here is how we will implement this to our activities:

class MainActivity : ComponentActivity(), AnalyticsLogger by AnalyticsLoggerImpl() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
registerLifecycleOwner(this)
}
}

We didn’t extend any parent class, we didn’t inherit anything, we used an implementation and we delegated the work to its corresponding delegation, in our case “AnalyticsLoggerImpl” and we just called a single line of code.

Now, whenever we use our delegation in any class that is actually a lifecycle owner, we will be able to log events without needing a huge parent base activity class that has multiple different responsbilities.

Let’s also create another delegation to handle new intents:

interface DeepLinkHandler {
fun handleDeepLink(activity: Activity, intent: Intent?)
}

class DeepLinkHandlerImpl: DeepLinkHandler {
override fun handleDeepLink(activity: Activity, intent: Intent?) {
// Handle the intent
}
}

Let’s now implement this in our main activity:

class MainActivity : ComponentActivity(),
AnalyticsLogger by AnalyticsLoggerImpl(),
DeepLinkHandler by DeepLinkHandlerImpl() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
registerLifecycleOwner(this)
}

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
handleDeepLink(this, intent)
}
}

As you know, you can only extend/inherit a class once in a class. But, you are totally free to implement as many class as you like.

No more anti-pattern.

--

--