Understanding Dependency Injection A Tiny Bit More

Today I had a minor epiphany. I was working on a unit test on a class method, and had a rather annoying time with it because the class (a ViewModel) uses an instance of a different class (a repository) to do things. There is a coroutine involved, so things got a bit hairy until I watched a tutorial video that essentially suggested a dependency required for testing to be added as parameter for the class’s constructor.

This required me to modify the actual class, and when I was done I just realized what this is about. It’s dependency injection!

Sure, I’ve written about it before (in an attempt to understand it), but it’s only when I’m actively working on adding unit tests that I experienced the actual value of D.I. Pretty neat!


What is Declarative UI?

Jetpack Compose is the latest Android UI development framework from Google that nowadays piques my interest. Jumping from using Java to Kotlin was for the most part an enjoyable experience: I feel I can be expressive and more efficient with Kotlin. I get that same feeling whenever I’m digging further into Jetpack Compose, compared to the usual method of using XML files for UI development (also known as the imperative paradigm).

I haven’t actually used Jetpack Compose in production, and in this post I’d like to get to know it better by nerding out on its paradigm, declarative UI.

What is Jetpack Compose? From the announcement post:

Jetpack Compose is a fully declarative component-based approach, meaning you describe your UI as functions that transform data into a UI hierarchy. When the underlying data changes, the Compose framework automatically updates the UI hierarchy for you, making it simple to build UIs easily and quickly.

(emphasis mine)

This matches this cool formula I found on Flutter’s “Start thinking declaratively” documentation:

A mathematical formula: UI = f(state). 'UI' is the layout on the screen. 'f' is your build methods. 'state' is the application state.

What’s the difference between declarative and imperative? Let’s start with digging linguistically. These are definitions from

declare [ dih-klair ]
to make known or state clearly, especially in explicit or formal terms

imperative [ im-per-uh-tiv ]
of the nature of or expressing a command; commanding.

If we bring these definitions back to UI development, what does it mean to declare/state a user interface, compared to commanding a user interface?

I found this illuminating example from a PDF lecture from MIT about Declarative UI. Declarative UI can be found within this HTML example:

<div id=“main”>
<div id=“toolbar”>
<img src=“cut.png”></img>
<textarea id=“editor”></textarea>

And here’s how a relatively similar UI can be done imperatively in Java:

JPanel main = new JPanel();
JPanel toolbar = new JPanel();
JButton button = new JButton();
JTextArea editor = new JTextArea();

The way I understand it, the HTML is about describing the UI I want to display. It’s the what. Conversely, the Java example has commands in it (create new toolbar, create new button, set the button label, and so on). It’s the how. The difference between what and how seems to be the simplest way to explain the declarative vs imperative paradigm.

Declarative paradigm used on UI development are pretty popular, lately. Prominent examples are:

  • ReactJS in JavaScipt development
  • SwiftUI in iOS
  • Jetpack Compose in Android
  • Flutter for multi-platform development

It is often described that using the declarative paradigm, the key idea is to think about how a UI component should look like under any possible states. For example: how should an input box looks like if it’s empty? What if the user entered invalid information?

When writing React, it’s often good not to think of how you want to accomplish a result, but instead what the component should look like in its new state.

“Declarative vs Imperative Programming”

Coming back to the formula above, it means that essentially we’ll be writing component builder functions where we declare how the component should look like, given some parameters for the state.

From Jonathan Romano:

Benefits you get from declarative programming are largely in making code read more naturally. It allows for your code to visually convey structure and meaning. It allows for the entirety of the intent of some code to be described in one place. It allows a developer to spend less time parsing and instead jump right to the answer of “what does this code do?” It can also mean abstracting away the details of complex APIs behind a cleaner interface, letting someone else deal with the complexity of making sure everything is executed properly.

With Android development, the common, imperative way is to use XML layout files to declare the UI components, then we mutate and manipulate them inside the Java/Kotlin activity/fragment/others classes. The structure and meaning are separated. They’re okay once I’m used to it, however the declarative paradigm indeed seems like an improvement.

Here’s an example code from Jetpack Compose’s tutorial:

fun NewsStory() {
    val image = imageResource(R.drawable.header)
        modifier = Modifier.padding(16.dp)
    ) {
        val imageModifier = Modifier
            .clip(shape = RoundedCornerShape(4.dp))

        Image(image, modifier = imageModifier,
                  contentScale = ContentScale.Crop)

        Text("A day in Shark Fin Cove")
        Text("Davenport, California")
        Text("December 2018")

It’s for creating a “news story” component with an image at the top, then some text below it. The fun part is, that is all that’s needed. There’s no XML layout file anymore, and to me they’re so much easier to read.

Of note, see how the “news story” component above is set as a function: fun NewsStory(), which reminds me again to Flutter’s formula UI = f(state). This encourages creations of custom UI components that can be reused whenever needed.


Building Custom TimePicker Dialog Preference in Android

In the spaced repetition app I’m building, I wanted to allow people to pick a time for the study reminder notifications to show up on. There is a built-in AndroidX Preference Library that is meant to help with this, however I found that there’s no built-in way to display a TimePicker dialog as part of the preference option. Some tinkering are still required, and here’s the solution that works for me.


Before heading to the code, here’s a quick recap of what will be needed. Think of them as the building blocks that have to connect together.

  1. A class that derives from DialogPreference. This part is for managing the Preference aspect of the dialog preference to be displayed, such as its message, buttons, as well as for saving and retrieving the value selected. The library has some built-in dialog preferences (EditTextPreference,  ListPreferenceMultiSelectListPreference), but no such thing as TimePickerPreference, so we’ll have to make ourselves here.
  2. A class that derives from PreferenceDialogFragmentCompat. This part is for managing the visual aspect of the dialog preference to be displayed. This is where we will actually create and display the TimePicker, for instance.
  3. XML layout for the main preference page. This is used to for the visual aspect of the preferences page that’s shown to the user.
  4. Lastly, a fragment that derives from PreferenceFragmentCompat, which essentially is a special type of fragment that’s meant to be used for displaying the preferences page based on the XML layout, and for wrangling various display options related to it.

1. TimePickerPreference

First thing we want to make is the TimePickerPreference. Here’s how my code looks like:

package black.old.spacedrepetitionowl
import android.content.Context
import android.util.AttributeSet
import androidx.preference.DialogPreference
// This class is used in our preference where user can pick a time for notifications to appear.
// Specifically, this class is responsible for saving/retrieving preference data.
class TimepickerPreference(context: Context?, attrs: AttributeSet?) : DialogPreference(context, attrs) {
// Get saved preference value (in minutes from midnight, so 1 AM is represented as 1*60 here
fun getPersistedMinutesFromMidnight(): Int {
return super.getPersistedInt(DEFAULT_MINUTES_FROM_MIDNIGHT)
// Save preference
fun persistMinutesFromMidnight(minutesFromMidnight: Int) {
override fun onSetInitialValue(defaultValue: Any?) {
summary = minutesFromMidnightToHourlyTime(getPersistedMinutesFromMidnight())
// Mostly for default values
companion object {
// By default we want notification to appear at 9 AM each time.
private const val DEFAULT_HOUR = 9

Above, I override onSetInitialValue() so that I can fill the preference’s summary with the actual time. The idea is to allow people to see the currently selected time, which should look like this:

A preference with the 10:30 value written in the summary area of the preference.

With custom dialog preference like this, I found that the summary area is what’s commonly used to show the current value of that preference.

The two functions persistMinutesFromMidnight() and getPersistedMinutesFromMidnight() are to be used for saving and retrieving the value related to this preference.

2. TimePickerPreferenceDialog

After the preference aspect is created, now we set up the actual dialog. Here’s the code that I have:

package black.old.spacedrepetitionowl
import android.content.Context
import android.os.Bundle
import android.view.View
import android.widget.TimePicker
import androidx.preference.PreferenceDialogFragmentCompat
class TimePickerPreferenceDialog : PreferenceDialogFragmentCompat() {
lateinit var timepicker: TimePicker
override fun onCreateDialogView(context: Context?): View {
timepicker = TimePicker(context)
return timepicker
override fun onBindDialogView(view: View?) {
val minutesAfterMidnight = (preference as TimepickerPreference)
timepicker.hour = minutesAfterMidnight / 60
timepicker.minute = minutesAfterMidnight % 60
override fun onDialogClosed(positiveResult: Boolean) {
// Save settings
if(positiveResult) {
val minutesAfterMidnight = (timepicker.hour * 60) + timepicker.minute
(preference as TimepickerPreference).persistMinutesFromMidnight(minutesAfterMidnight)
preference.summary = minutesFromMidnightToHourlyTime(minutesAfterMidnight)
companion object {
fun newInstance(key: String): TimePickerPreferenceDialog {
val fragment = TimePickerPreferenceDialog()
val bundle = Bundle(1)
bundle.putString(ARG_KEY, key)
fragment.arguments = bundle
return fragment

Inside onCreateDialogView(), we generate the TimePicker to be displayed.

Inside onBindDialogView(), we grab the current hour/minute preference value, and set that value into the TimePicker. The goal is to make it so that when someone first opens the TimePicker dialog, the clock already shows the current value instead of starting from zero. (preference as TimepickerPreference) is needed here because without it, the compiler does not now that the current preference is a custom TimePickerPreference, and hence will not be able to find the function we need.

Inside onDialogClosed(), we save the value selected by the user after clicking the positive button.

For more information about the NewInstance pattern, this article will be helpful.

3. XML Layout

Here’s my preference XML file:

<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="">
android:title="Notification time"
android:summary="This will programmatically show current notification time."
android:defaultValue="90" />
view raw preference.xml hosted with ❤ by GitHub

The interesting part might be <black.old.spacedrepetitionowl.TimepickerPreference> which indeed is the class we created on step 1. During run-time, the android:summary part is programmatically modified and filled in both within TimePickerPreference (on initial opening of the preference screen), and TimePickerDialogPreference (after user picked a time).

4. PreferenceFragment

This is the regular fragment that we’ll use to display the preference screen. Here’s my file:

package black.old.spacedrepetitionowl
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
* A simple [Fragment] subclass.
class PreferenceFragment : PreferenceFragmentCompat() {
private val DIALOG_FRAGMENT_TAG = "TimePickerDialog"
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preference, rootKey)
override fun onDisplayPreferenceDialog(preference: Preference?) {
if(preference is TimepickerPreference) {
val timepickerdialog = TimePickerPreferenceDialog.newInstance(preference.key)
timepickerdialog.setTargetFragment(this, 0), DIALOG_FRAGMENT_TAG)
else {

First, we’re pulling the XML layout inside onCreatePreferences().

Next, since we want to display our own custom dialog preference, we need to override onDisplayPreferenceDialog(). Here, we specifically want to check if the currently selected preference is our custom preference, TimePickerPreference. If it is, we instantiate a new TimePickerPreferenceDialog and display it. If it’s a different type of dialog preference (let’s say it’s one of the three built-in dialog preferences mentioned earlier), we let it be handled automatically by the superclass.

And that’s it! This is just a small example but hopefully is a good starting point if you want to create a different kind of custom dialog preference 🙂


Fun with ViewModel and LiveData: Sending Informations between Fragments

In Android development, there are a few different ways to send informations between one fragment to another. As of September 2020, the recommended way to allow for this communication is to use LiveData exposed by a shared ViewModel.

The steps are relatively simple. Suppose that we want to send data from Fragment A to Fragment B:

  1. Create a ViewModel
  2. Have Fragment A and Fragment B share that same ViewModel.
  3. Create a LiveData inside the ViewModel to contain the data that’s to be passed from A to B.
  4. Assign or update value to the LiveData object inside Fragment A, usually with the help of a public method inside the ViewModel to assign or update the value.
  5. Have Fragment B observe the LiveData inside it onCreate() , and make use of the data there.

Resources for Learning about Jetpack Compose

Jetpack Compose is Google’s latest toolkit for building UI, at the moment generally aimed for Android development. Below is a list of what I think is a good, comprehensive approach on how to learn about it. At the moment, Jetpack Compose is under active development and currently released in Alpha version, so this list will continuously evolve with it as well.

Understanding Jetpack Compose — Part 1 of 2 by Leland Richardson, one of Google’s engineers developing Jetpack Compose. The post helps explain the “why” of Jetpack Compose. What challenges is it trying to address, and how.

Thinking in Compose. Also helps explain why the declarative UI model as used by Jetpack Compose is beneficial.

Official documentation

  • Layouts in Compose. Explains how layouts can be created using Jetpack Compose and shows the built-in components that come with it.
  • Theming in Compose. Explains how to modify the look-and-feel of an app made with Jetpack Compose.
  • Testing in Compose. How to test UI made with Jetpack Compose.
  • Interoperability. Explains how to add, use, and combine Jetpack Compose with existing apps that already use the view-based UI model.

Sample apps

  • . Official samples from the Android repository. Has multiple different sample apps in it, with the Readme file helpfully explaining what concepts are being explored and used in each app.

Also, Elsewhere

  • React. A declarative UI library written in JavaScript, mostly used on web development.
  • SwiftUI. A declarative UI library for development within the Apple platform.

Getting to Know Hilt: Dependency Injection Library for Android

Artikel ini juga tersedia dalam Bahasa Indonesia.

I’ve written about dependency injection (D.I) a couple of times on this blog. First about understanding Dagger (in Indonesian) and second about a simple way to understand D.I. and why it is needed. The latest development is that Google is now recommending a new library for implementing D.I. It’s called Hilt:

Technically the video above is plenty enough to introduce Hilt, so at this point I could have ended this article 😆

Nevertheless, I’ll try to make a shorter summary below with the hope that it will make it simpler to understand from a beginner’s perspective.

Essentialy, Hilt is meant to help with adding D.I. in our application. There’s some automated processes that it does so that we don’t have to manually add dependency injection.

Why should we have Hilt when Dagger already exists? From what I understand, the main idea is to simplify. And Hilt itself is made with Dagger at its base. According to Dagger’s site:

Hilt provides a standard way to incorporate Dagger dependency injection into an Android application.

The goals of Hilt are:

– To simplify Dagger-related infrastructure for Android apps.

– To create a standard set of components and scopes to ease setup, readability/understanding, and code sharing between apps.

– To provide an easy way to provision different bindings to various build types (e.g. testing, debug, or release).

For those who haven’t went deep with Dagger, I think learning Hilt instead will be more efficient and pragmatic (unless if in practice you will be dealing with existing codebases that already use Dagger.)

If we look at the new navigation menu hierarchy on the Dependency Injection page on the Android training page, we can see that Hilt is shown earlier than Dagger, giving it a bit more perceived importance than Dagger.

Basic Concepts for D.I. Frameworks

If you haven’t learned about Dependency Injection whatsoever, please open and read this article first, before continuing here.

The common technique in creating D.I. is by using the idea of container. When creating D.I. manually, container will be a class that is supposed to be the central place where all dependencies are generated. An example from Android training:

// Container of objects shared across the whole app
class AppContainer {

    // Since you want to expose userRepository out of the container, you need to satisfy
    // its dependencies as you did before
    private val retrofit = Retrofit.Builder()

    private val remoteDataSource = UserRemoteDataSource(retrofit)
    private val localDataSource = UserLocalDataSource()

    // userRepository is not private; it'll be exposed
    val userRepository = UserRepository(localDataSource, remoteDataSource)

Next, the class is instantiated within Application(), which acts as the entry point of the app, so that subsequent components like fragments or activites can use the container to get their dependencies.

At its core, a D.I. framework like Hilt is supposed to make the process above easier. It will automatically generate the container class for us, as well as help inject the dependencies (hence why it’s called Dependency Injection) into the components that need them.

Basic Concepts for Hilt

Roughly speaking, first we need to teach Hilt a few things to it can automate things for us. As commonly used in Android programming, we use annotation for this.

First, the @HiltAndroidApp annotation is used to teach Hilt where the Application() instance is in our app:

class ExampleApplication : Application() { ... }

The second annotation that’s needed is @AndroidEntryPoint. It is used to signify components that will need some injections:

class ExampleActivity : AppCompatActivity() { ... }

The third important annotation is @Inject. It has two roles. First, it is used to “tag” dependencies that will eventually be injected into components:

class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

Second, it is also used within components to actually do the injection:

class ExampleActivity : AppCompatActivity() {
 @Inject lateinit var analytics: AnalyticsAdapter

Going Forward with Hilt

The three annotations above are the most basic things we need to get started with Hilt. Going further, there are different cases that will require further setup with Hilt. Cases such as:

  • D.I. related with interface
  • D.I. for dependencies with third-party or external libraries like Retrofit or Room database
  • D.I. with more than one implementation for the same type to be injected.

The cases above are beyond this current article. There is a docunemtation page here for further information.

References and Sources

Bahasa Indonesia Programming

Perkenalan dengan Hilt: Library Dependency Injection untuk Android

Saya sudah beberapa kali menulis soal dependency injection (D.I.) di blog ini. Masih yang basic-basic saja, pertama soal memahami Dagger dan kedua soal cara sederhana memahami D.I dan mengapa dia dibutuhkan. Berita terbaru, ada library baru dari Google untuk membantu mempermudah mengaplikasikan D.I. di app kita. Namanya Hilt:

Sebenarnya video di atas sudah sangat cukup untuk memperkenalkan Hilt, jadi sampai di sini artikel ini sebenarnya bisa ditutup 😆

Tetapi berikut saya akan mengikhtisarkan sedikit dengan harapan agar dia lebih mudah dipahami lagi.

Jadi Hilt pada intinya difungsikan untuk membantu proses menambahkan D.I. di aplikasi kita. Ada proses otomatisasi di dalamnya agar kita tidak perlu melakukan D.I. secara manual.

Mengapa harus ada Hilt kalau sudah ada Dagger? Di sini sepertinya kata kuncinya adalah penyederhanaan. Hilt sendiri dibuat menggunakan Dagger sebagai basisnya. Menurut situs Dagger:

Hilt provides a standard way to incorporate Dagger dependency injection into an Android application.

The goals of Hilt are:

  • To simplify Dagger-related infrastructure for Android apps.
  • To create a standard set of components and scopes to ease setup, readability/understanding, and code sharing between apps.
  • To provide an easy way to provision different bindings to various build types (e.g. testing, debug, or release).

Hilt menyediakan cara standar untuk menambahkan Dagger dependency injection ke dalam sebuah aplikasi Android.

Tujuan Hilt antara lain:

  • Menyederhanakan infrastuktur yang terkait dengan Dagger dalam aplikasi Android
  • Membuat set komponen dan scopes yang standar untuk memudahkan dalam setup, membaca/memahami kode, dan berbagi kode dalam banyak aplikasi.
  • Menyediakan cara yang mudah untuk membuat binding yang berbeda-beda untuk build types yang berbeda (misalkan unutk testing, debug, atau release).

Bagi yang belum terlalu mendalami Dagger, saya rasa mempelajari Hilt akan lebih efisien dan pragmatis (kecuali jika pada prakteknya harus berhadapan dengan kode yang dari awal sudah menggunakan Dagger).

Jika kita melihat dari hierarki menu di halaman Dependency Injection di Android training milik Google, Hilt ditampilkan lebih awal dari Dagger, yang sepertinya secara halus menunjukkan kalau Google ingin mendorong orang-orang untuk memakai Hilt.

Konsep dasar D.I. Frameworks

Bagi yang belum paham D.I. sama sekali, silakan membaca artikel ini dulu, baru kembali ke sini.

Teknik umum dalam membuat D.I. adalah dengan menggunakan konsep kontainer. Apabila membuat D.I. secara manual, kontainer adalah class yang bertugas menjadi tempat semua dependensi dibuat. Contoh dari Android:

// Container of objects shared across the whole app
class AppContainer {

    // Since you want to expose userRepository out of the container, you need to satisfy
    // its dependencies as you did before
    private val retrofit = Retrofit.Builder()

    private val remoteDataSource = UserRemoteDataSource(retrofit)
    private val localDataSource = UserLocalDataSource()

    // userRepository is not private; it'll be exposed
    val userRepository = UserRepository(localDataSource, remoteDataSource)

Kemudian, class ini dibuat instance-nya di dalam Application(), yang bertindak sebagai pintu masuk aplikasi, sehingga komponen-komponen berikutnya seperti fragments atau activities bisa menggunakan kontainer tersebut untuk membuat dependensi.

Sebuah D.I. framework seperti Hilt pada intinya bertugas membuat proses di atas lebih mudah. Dia bisa membuatkan class kontainer untuk kita, sekaligus “menyuntikkan” dependensi (dari sini istilah Injection di D.I. berasal) ke dalam komponen-komponen yang membutuhkannya.

Konsep dasar Hilt

Secara kasar, kita perlu memberi tahu Hilt bagaimana caranya agar dia bisa membuatkan kontainer dan menginjeksi dependensi secara otomatis. Caranya, seperti yang umum digunakan di pemrograman Android, adalah dengan menggunakan anotasi/annotation.

Pertama, anotasi @HiltAndroidApp untuk memberi tahu Hilt di mana Application() instance di aplikasi kita:

class ExampleApplication : Application() { ... }

Anotasi kedua yang diperlukan adalah @AndroidEntryPoint. Ini untuk memberi tanda kepada komponen yang membutuhkan injeksi:

class ExampleActivity : AppCompatActivity() { ... }

Anotasi ketiga yang juga penting adalah @Inject. Dia punya dua tugas. Pertama, untuk memberi tanda kepada dependensi-dependensi yang nantinya perlu diinjeksi ke dalam sebuah komponen:

class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

Kedua, dia juga dipakai di dalam komponen di mana injeksi harus dilakukan:

class ExampleActivity : AppCompatActivity() {
 @Inject lateinit var analytics: AnalyticsAdapter

Lebih Lanjut dengan Hilt

Tiga jenis anotasi di atas pada dasarnya sudah cukup untuk memulai menggunakan Hilt. Lebih lanjut, ada kasus-kasus lain yang membutuhkan setup lebih banyak untuk dikerjakan dengan Hilt. Kasus-kasusnya seperti:

  • D.I. terkait dengan interface
  • D.I. untuk dependensi yang berasal dari third-party atau library eksternal seperti Retrofit atau Room database
  • D.I. dengan lebih dari satu implementasi untuk satu type yang sama

Artikel ini tidak membahas kasus-kasus di atas. Ada dokumentasi lebih lanjut di sini bagi yang tertarik.

Referensi dan Sumber-sumber


“What Hacker News Says” Extension

Hacker News has been one of the places with high signal-to-noise ratio for discussions, for me. The majority of its contents is tech-related, although different topics do show up from time to time.

The “What Hacker News Says” browser extension (available for Firefox and Chrome/Chromium browsers) is a pretty cool thing that helps with a particular use case: has there been any discussion in Hacker News about this particular webpage I’m currently opening?

Pretty cool! And, yes, I found the news about it from Hacker News.


Beginner Series: Dependency Injection

Dependency Injection is a concept that did not immediately sound intuitive to my mind, especially as a non-native English speaker. I’ve read a bunch of different articles and resources about it, and found two that helped.

First, Dependency Injection Demystified. The article is quite short and I highly recommend going through it:

Dependency injection means giving an object its instance variables. Really. That’s it.

To understand what “dependency” means, here’s the famous graph about Android’s recommended app architecture:

Android's recommended app architecture

The arrows in the graph there are the dependencies. So Room depends on SQLite, the Repository depends on the Model, the ViewModel depends on the Repository, and the Activity/Fragment depends on the ViewModel.

The simplest form of dependency injection is like the primary constructor with parameter in Kotlin, like the following example:

class Person constructor(favoriteBook: Book) { /*...*/ }

Here, favoriteBook is the dependency, while the constructor parameter is the way to inject it into the Person object. This is in contrast with having the favoriteBook object created within the class.

The next question in my mind was, why do we need this?

This StackOverflow answer was the second resource that helped me with these two reasons:

Two important ones are the ability to control functionality from a central place (the Main() function) instead of spreading it throughout your program…

I think this is pretty obvious.

…and the ability to more easily test each class in isolation (because you can pass mocks or other faked objects into its constructor instead of a real value).

For testing purposes, it’s good if we can test the Person class individually and send dummy object to it (the favoriteBook object on the example above). Or, as the official manual dependency injection guide mentioned:

Dependency injection helps make these connections and enables you to swap out implementations for testing. For example, when testing a ViewModel that depends on a repository, you can pass different implementations of Repository with either fakes or mocks to test the different cases.

And the final question: if dependency injection is this simple, why are there complex tools made for it?

This is turns out relates to the first reason above:

The drawback, of course, is that you now have one mega-function that knows about all the classes used by your program. That’s what DI frameworks can help with.

Once a codebase gets really big and complex, it’s easy to imagine that manual dependency injections can go all over the place. The tools/frameworks eventually are meant to help with that.


Common Software Metaphors

A confusing abundance of metaphors has grown up around software development. David Gries says writing software is a science (1981). Donald Knuth says it’s an art (1998). Watts Humphrey says it’s a process (1989). P. J. Plauger and Kent Beck say it’s like driving a car, although they draw nearly opposite conclusions (Plauger 1993, Beck 2000). Alistair Cockburn says it’s a game (2002). Eric Raymond says it’s like a bazaar (2000). Andy Hunt and Dave Thomas say it’s like gardening. Paul Heckel says it’s like filming Snow White and the Seven Dwarfs (1994). Fred Brooks says that it’s like farming, hunting werewolves, or drowning with dinosaurs in a tar pit (1995).

Code Complete 2