Why use Dagger or Hilt DI when I can use static functions?

Viewed 1013

I am learning DI with Dagger2 and Hilt and I've got a question: When creating android app I notice that I use lots of utility classes with static methods(i.e. a function that receives temperature in Celsius and return it in Fahrenheit). BUT, also I use a class (say NetworkUtils) with a static utility method for performing network call to get data from API (as what Android Nanodegree course's instructors were doing): What I do is like:

class NetworkUtils{
    public static String fetchCityName(double latitude, double longitude){
        // code for API call
    }
}

And now, while I am learning DI principles from Developer Docs, I notice that network calls are made in an instance class within an instance method and its parameters are injected using Dagger.

  1. Why does this make difference from what I was doing? I read that static methods make testing not easy, however, suppose I used DI like the docs shows,
  2. Why do I have to instantiate a new object of the NetworkUtils whenever I need to perform API call while creating multiple instances is useless? Also, the official docs says that our use of @Singleton annotation to set function scope (i.e. for a REST API call function) should be very rare, then:
  3. Am I supposed to create instances for everything I need to use even tho it doesn't need to? (except for expensive object instantiation)
  4. Eventually, could you please clarify the difference between a utility class and a normal class that I should not use static methods in it?

Thanks.

1 Answers

And now, while I am learning DI principles from Developer Docs, I notice that network calls are made in an instance class within an instance method and its parameters are injected using Dagger.

  1. Why does this make difference from what I was doing? I read that static methods make testing not easy, however, suppose I used DI like the docs shows,

I see two main reasons:

First, of course, is that the static calls make the code hard to test. But yes, there are some testing frameworks that can do the work for you and mock static calls. But whenever you are using them - it most likely means that you are doing something wrong or you are working with legacy code. This leads me to the second topic.

The idea of TDD - Test Driven Development is that you write the tests first, the code second. But the tests "are not important". Actually, the real result is the better code! The unit test just helps, but they are subproducts. Most of the time there is a high correlation between a good code and a testable code. When you have static calls and singletons it is easy for you to call this code from wherever you want. The result is that you stop thinking about design, about the real OOP principles - what is what. You do not think about extra objects, the relation between objects, etc. And you end up with a spaghetti code. Everything is tied to everything else.

  1. Why do I have to instantiate a new object of the NetworkUtils whenever I need to perform API call while creating multiple instances is useless? Also, the official docs says that our use of @Singleton annotation to set function scope (i.e. for a REST API call function) should be very rare, then:

You are saying something which is contradictory - "creating multiple instances is useless". Obviously creating an instance and not using a static call helps you to achieve better and testable code. Maybe you are wondering if it is a performance hit. Here I will quote.. maybe Martin Fowler... but in his book about Refactoring he is explaining that 9 out of 10 times the optimizations are just useless. You are overdoing it. You need to write good code. Then evaluate. Then if there is a need for optimization - you have the good code - the optimization will be relatively easy.

In your case - do not worry. Wrapping the network calls in instance classes will cause how many instances? 3? 5? It is not a problem when you evaluate the benefits.

About the Singleton - you do not need a shared state - so no need for a singleton. You will shoot yourself in the foot. You will keep things that can be garbage collected, add extra code for singleton creation, etc. Just plain object and that's all.

  1. Am I supposed to create instances for everything I need to use even tho it doesn't need to? (except for expensive object instantiation)

This is a huge question. You should read a few books and still will not know the answer. Try with Uncle Bob's Clean Code and also Martin Fowler's Refactoring. Then read a few articles of people who think that they are overdoing it and find the balance for yourself. In general, you should no be using "new" and any static calls.. But for example, there are a lot of discussions about how useful are really unit tests on Android and there are a lot of people who tend to go in the direction that you need mostly integration and End to End tests. I will not argue what is the right approach.

 4. Eventually, could you please clarify the difference between a utility class and a normal class that I should not use static methods
 in it?

Basically, you should not be using "Utility classes". Their idea is to wrap some code, which is used in many places, but there is no state involved. You do not need instance variables so the static methods were a good idea. But it will totally kill your testing. So in your case, you should make one type of class and if you need state - add, if you do not need state - just don't. Then you can call the classes whatever you want.

Related