← Back to list

Mastering Creational Design Patterns in Kotlin & Android: Singleton, Factory & Abstract Factory

Introduction

chetan shingare · 2025-03-06 14:15 · 3 claps · 3.9 min read
#creational-design-pattern #creational-pattern-kotlin #android-design-patterns #creational-design-kotlin
Open on Medium ↗
Wiki topics: TLS · Design Tools & Workflow 📱 · Mobile Development

Mastering Creational Design Patterns in Kotlin & Android: Singleton, Factory & Abstract Factory

Introduction

Object creation in software development can become complex and inefficient if not handled properly. Hardcoded object creation leads to tightly coupled code, making maintenance, testing, and scalability difficult.

This is where Creational Design Patterns help by providing flexible and efficient object creation mechanisms.

Why Learn Creational Design Patterns?

✅ Better Object Management — Avoid unnecessary object creation. ✅ Loose Coupling — Reduces dependency between components. ✅ Scalability — Makes it easier to add new types without modifying existing code. ✅ Testability — Simplifies unit testing and mocking dependencies.

This guide covers: 🔹 Singleton Pattern — Ensures only one instance of a class exists. 🔹 Factory Pattern — Creates objects without exposing creation logic. 🔹 Abstract Factory Pattern — Provides a higher-level abstraction for object creation.

Let’s dive into Creational Patterns and explore how to implement them in Kotlin and Android. 🚀

1️⃣ Singleton Pattern

What is the Singleton Pattern?

Singleton ensures that only one instance of a class exists throughout the application and provides a global access point to that instance.

✅ Key Features: ✔ Single Instance — Only one object exists in memory. ✔ Global Access Point — The instance is accessible from anywhere. ✔ Thread Safety — Ensures safe multi-threaded access.

When to Use Singleton?

✔ When only one instance of a class is needed across the app. ✔ To prevent multiple resource-heavy instances (e.g., database connections, network calls). ✔ For shared state or cache management.

How to Identify the Need for Singleton?

🔹 If you see multiple objects holding the same state, Singleton can centralize that state. 🔹 If an object must be shared globally (e.g., database, logging system).

Singleton in Kotlin (Implementation & Test)

object NetworkDriver {
    init {
        println("Network Driver Initialized: $this")
    }
    fun log(): NetworkDriver = apply { println("Logging from: $this") }
}

class SingletonTest {
    @Test
    fun testSingleton() {
        println("Start Singleton Test")
        val networkDriver1 = NetworkDriver.log()
        val networkDriver2 = NetworkDriver.log()

        assertThat(networkDriver1).isSameAs(NetworkDriver)
        assertThat(networkDriver2).isSameAs(NetworkDriver)
    }
}

Alternative Ways to Implement Singleton in Kotlin

1️⃣ Using Lazy Initialization

class Database private constructor() {
    companion object {
        val instance: Database by lazy { Database() }
    }
}

2️⃣ Using Synchronized Block (Thread-safe Singleton)

class Logger private constructor() {
    companion object {
        @Volatile
        private var instance: Logger? = null

        fun getInstance(): Logger {
            return instance ?: synchronized(this) {
                instance ?: Logger().also { instance = it }
            }
        }
    }
}

✅ Android Use Cases: ✔ SharedPreferences management ✔ Database instance (Room Database) ✔ Networking (Retrofit instance)

2️⃣ Factory Pattern

What is the Factory Pattern?

Factory Pattern provides a way to create objects without exposing creation logic. Instead of using new, it delegates object creation to a factory method.

✅ Key Features: ✔ Encapsulates object creation logic. ✔ Promotes loose coupling. ✔ Improves testability and maintainability.

When to Use Factory Pattern?

✔ When multiple subclasses share a common interface but need different implementations. ✔ When object creation depends on user input or runtime conditions. ✔ When you don’t want clients to directly instantiate objects.

How to Identify the Need for Factory?

🔹 If object creation logic varies based on input, consider a Factory. 🔹 If you see when or if-else blocks used for instantiating classes, Factory helps reduce repetition.

Factory in Kotlin (Implementation & Test)

sealed class Country {
    object Canada : Country()
    object Spain : Country()
    class Greece(val prop: String) : Country()
    data class USA(val prop: String) : Country()
}

class Currency(val code: String)

object CurrencyFactory {
    fun currencyForCountry(country: Country): Currency =
        when (country) {
            is Country.Spain -> Currency("EUR")
            is Country.USA -> Currency("USD")
            is Country.Greece -> Currency("EUR")
            is Country.Canada -> Currency("CAD")
        }
}

class FactoryMethodTest {
    @Test
    fun currencyTest() {
        val greeceCurrency = CurrencyFactory.currencyForCountry(Country.Greece("")).code
        val usaCurrency = CurrencyFactory.currencyForCountry(Country.USA("")).code
        assertThat(greeceCurrency).isEqualTo("EUR")
        assertThat(usaCurrency).isEqualTo("USD")
    }
}

✅ Android Use Cases: ✔ ViewModel Factory (Creating ViewModels dynamically) ✔ UI Component Factories (Button, AlertDialog, RecyclerView Adapters)

Using Factory Pattern for ViewModel Factory in Android

In Android MVVM architecture, we often need to create instances of ViewModel classes with dependencies. However, ViewModels do not have default constructors, so ViewModelProviders require a custom ViewModelFactory to instantiate them properly.

Custom ViewModel Factory Implementation in Kotlin

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider

// ViewModel that requires dependencies
class UserViewModel(private val repository: UserRepository) : ViewModel() {
    fun getUserData(): String = repository.getUser()
}

// ViewModel Factory using Factory Pattern
class ViewModelFactory(private val repository: UserRepository) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
            return UserViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

// Example Usage
val factory = ViewModelFactory(UserRepository())
val userViewModel = ViewModelProvider(viewModelStore, factory).get(UserViewModel::class.java)

✅ Benefits of Using Factory Pattern for ViewModels: ✔ Decouples ViewModel instantiation from UI components. ✔ Enables dependency injection. ✔ Makes testing ViewModels easier.

3️⃣ Abstract Factory Pattern

What is the Abstract Factory Pattern?

Abstract Factory adds an extra layer of abstraction over the Factory Pattern. It provides a way to create families of related objects without specifying their concrete classes.

✅ Key Features: ✔ Encapsulates multiple factories. ✔ Allows easy swapping of object families. ✔ Promotes scalability and testability.

When to Use Abstract Factory?

✔ When an application needs multiple factories for creating related objects. ✔ When object creation must be handled dynamically. ✔ When you need flexibility to switch between object families.

Abstract Factory in Kotlin (Implementation & Test)

interface DataSource

class DataBaseDataSource : DataSource
class NetworkDataSource : DataSource

abstract class DataSourceFactory {
    abstract fun makeDataSource(): DataSource

    companion object {
        inline fun <reified T : DataSource> createFactory(): DataSourceFactory =
            when (T::class) {
                DataBaseDataSource::class -> DataBaseFactory()
                NetworkDataSource::class -> NetworkFactory()
                else -> throw IllegalArgumentException()
            }
    }
}

class DataBaseFactory : DataSourceFactory() {
    override fun makeDataSource() = DataBaseDataSource()
}

class NetworkFactory : DataSourceFactory() {
    override fun makeDataSource() = NetworkDataSource()
}

class AbstractFactoryTest {
    @Test
    fun abstractFactoryTest() {
        val dataSourceFactory = DataSourceFactory.createFactory<DataBaseDataSource>()
        val dataSource = dataSourceFactory.makeDataSource()
        assertThat(dataSource).isInstanceOf(DataBaseDataSource::class.java)
    }
}

✅ Android Use Cases: ✔ Data sources (Local vs. Remote databases) ✔ Theme-based UI rendering (Light/Dark mode components)

Final Thoughts

The Creational Design Patterns (Singleton, Factory, Abstract Factory) help improve object creation, scalability, and maintainability in Kotlin and Android development.

💡 Try implementing these patterns in your next Kotlin project!

🚀 Follow for more Kotlin and Android design pattern insights!


메타데이터
post_id
41805e6d499d
slug
mastering-creational-design-patterns-in-kotlin-android-singleton-factory-abstract-factory-41805e6d499d
url
https://medium.com/@chetanshingare2991/mastering-creational-design-patterns-in-kotlin-android-singleton-factory-abstract-factory-41805e6d499d
canonical_url
https://medium.com/@chetanshingare2991/mastering-creational-design-patterns-in-kotlin-android-singleton-factory-abstract-factory-41805e6d499d
author_url
https://medium.com/@chetanshingare2991
status
ok
fetched_at
2026-06-09 15:37:30