Loading...
Home
  • Tech Blogs
  • Videos
  • Conferences
    • Droidcon News
    • Upcoming Conferences
    • Become a Partner
    • Past Events
    • Keep Me Informed
    • Diversity Scholarships
  • Community
    • droidcon Team
    • How to Hold a Droidcon
  • Android Careers
Sign In

Global CSS

 

The forgotten art of construction

 

 
Danny Preussler
Android @ Soundcloud, Google Developer Expert, Conference Nomad, Geek, Goth, Cyborg, Ex @Viacom@Groupon @eBay ♡ Kotlin ♡ TDD ♡ Clean Code
Published: July 17, 2020
Tweet
Share
 

How tools made us forget how to write sane constructors

 

https://unsplash.com/photos/qvBYnMuNJ9A

 

In an ideal world, developers get smarter every day. The code we write this year should be better than the code we wrote 10 years ago, which in turn should be better than the code 20 years ago. Today we have better tools, more modern languages, and better practices.

But as often in life, we realize we are not living in that ideal world. In nearly every codebase I see today, there are things that would shock a developer two decades ago. I am speaking of what Mark Seemann called “Constructor Over Injection”.

We’ve all learned to inject our dependencies into constructors. And ideally use a tool for that like Spring or Dagger.

If you look at some random classes from your codebase, how many fields are you injecting? Three? Five? More? I’m pretty sure you easily can find classes with even more, like this one:

 

class ProfilePresenter
@Inject
constructor(
    @MainThreadScheduler private val mainScheduler: Scheduler,
    @IOScheduler private val ioScheduler: Scheduler,
    private val profileApi: ProfileApi,
    private val userRepository: UserRepository,
    private val analytics: Analytics,
    private val errorReporter: ErrorReporter
    private val referrerTracker: ReferrerTracker,
    private val shareTracker: ShareTracker,
    private val tracksRepository: TracksRepository,  
    private val playlistRepository: PlaylistRepository
)

 

If you would show this constructor to a developer from 20 years ago they would probably look at you as if you would be crazy. No one would want to call this constructor and provide all these parameters.

But these days we don’t care. We don’t have to. We have a tool that will provide us with all those parameters, right?

This does not make it right though!

 

The proof

If you would use some manual injection code or a service locator like Koin, you would notice more what’s going on because you would need to write code like this:

 

ProfilePresenter(get(), get(), get(), get(), get(), get(), get(), get(), get(), get())

 

This makes the insanity of these constructors obvious!

 

get(), get(), get(), get(), get()… © giphy.com

 

Testing this class is also no fun anymore as you need to construct or mock a lot of instances.

So what is the underlying problem here?

 

Single Responsibility

Large constructors are a code smell, not a problem per se. But as with most code smells, they give you a hint that something is wrong. It’s the Single Responsibility Principle that is being violated.

If your class needs ten parameters, there is a high risk that your class is doing more than one thing. Why would it need all these objects otherwise?

So the obvious question now is: how many constructor arguments are acceptable?

Uncle Bob argues in Clean Code:

 

The ideal number of arguments for a function is zero (niladic). Next comes one (monadic) followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification — and then shouldn’t be used anyway.

 

That sounds tough, doesn’t it?

There is some valid theory behind this principle. In 100 Things Every Designer Needs to Know about People the author writes, that according to studies:

 

People can hold three or four things in working memory as long as they aren’t distracted and their processing of the information is not interfered with.

 

So if you need a max number as a general rule of thumb: make it less than four!

How do we decrease the number of our constructors then?

If you ask the question or look at StackOverflow, often the first answer you will get is: use the Builder pattern!

This does not really help us here though. The complexity of our class would not decrease. And creating an instance of our class is not our problem, our dependency injection library handles this for us anyway.

 

Extract common dependencies

I would start to look for things that belong together. In our example above we were injecting 2 schedulers:

 

@MainThreadScheduler private val mainScheduler: Scheduler,
@IOScheduler private val ioScheduler: Scheduler,

 

It is a good practice grouping those into sth like:

 

interface RxSchedulers {
    val io: Scheduler
    val computation: Scheduler
    val main: Scheduler
}

 

Facades

In many other cases simply extracting some fields into some wrapper objects will not help much.

Mark Seemann would call this solution Deodorant:

 

Reducing thirteen constructor arguments to a single Parameter Object doesn’t address the underlying problem. It only applies deodorant to the smell.

 

What we can do is hiding some of the details. Again, look for classes that do similar things:

 

private val analytics: Analytics,
private val referrerTracker: ReferrerTracker,
private val shareTracker: ShareTracker

 

These seem all related to tracking. Our class does not need to do know all the details! Hide them behind a Facade and expose those only those methods you need here.

 

class ProfileTracker(
   private val analytics: Analytics,
   private val referrerTracker: ReferrerTracker,
   private val shareTracker: ShareTracker   fun trackProfileOpened(referrer: String) = 
      referrerTracker.profileOpened(referrer)   fun trackProfileShared() = 
      shareTracker.profileShared()}

 

A good thing to keep in mind is that our presenter should have no idea that it's tracking to Firebase for example. That is an implementation detail! You don't want to add another field just because you add another analytics library. If you see something like that, extract and encapsulate:

 

interface Analytics {...}class DefaultAnalytics
@Inject
constructor(
    private val firebaseAnalytics: FirebaseAnalytics,
    private val mixPanel: MixpanelAPI
): Analytics

 

Via Interfaces

Remember that a class can implement multiple interfaces! You could build facades simply by splitting into interfaces.

 

class DefaultAnalytics : ScreenTracker, ShareTracker, ProfileTracker

 

Your favorite dependency injection library handles that for you without exposing details. Example for Dagger:

 

@Binds abstract fun bindScreenTracker(analytics: DefaultAnalytics): ScreenTracker@Binds abstract fun bindShareTracker(analytics: DefaultAnalytics): ShareTracker@Binds abstract fun bindProfileTracker(analytics: DefaultAnalytics): ProfileTracker

 

Use cases

In my experience classes like Presenters or ViewModels, which interact with views, tend to become large. You will often find that those dealing with multiple repositories:

 

    private val userRepository: UserRepository,
    private val tracksRepository: TracksRepository,  
    private val playlistRepository: PlaylistRepository,

 

Like shown earlier, those can be combined. But users, tracks and playlists don’t really have something in common, so maybe a simple facade repository is not the best way. As we probably need them in a very specific way for profiles, Clean Architecture suggests the UseCases design pattern here.

 

class ProfileUseCase(
   private val userRepository: UserRepository,
   private val tracksRepository: TracksRepository,  
   private val playlistRepository: PlaylistRepository,
)

 

This way you can simply expose what is needed for the profile “use case”.

 

Split it up

At some point, you will end up with arguments that don’t have much in common. This is the point when you should revisit whats the purpose of your class. What is it’s responsibility? Then start splitting it up!

Uncle Bob gives the tip for the Single Responsibility Principle:

 

Gather together the things that change for the same reasons. Separate those things that change for different reasons.

 

Some developers don’t like the idea of having multiple view models or presenters. This is a matter of taste and your architecture, you can combine them into one that is the source of truth for your view.

 

Sum up

As with other things all these rules are guidelines. You probably do not want to start with Facades and Use Cases on the green field.

 

Image for post

© Walt Disney Company

 

Those large constructors normally grow over time and end up being the monster we saw. To be sure to not let the creep in, here are some things you might want to consider checking regularly:

  • Try to stay below four parameters
  • Group things together that belong together
  • Check if your class has only one responsibility, otherwise, start splitting

Important is that you keep some sanity!

 

On Constructor Over-injection

Constructor Over-injection is a code smell, not an anti-pattern. Sometimes, people struggle with how to deal with the…

blog.ploeh.dk

 

Thanks to Jovche Mitrejchevski, Erik Hellman, and Aidan Mcwilliams. 

 

 

Tags: Kotlin, Clean Code, Dependency Injection, Android, Code Smells

 

View original article at: 


 

Originally published: June 22, 2020

Android News
Evolution of Android Update SystemEvolution of Android Update System
Evolution of Android Update SystemEvolution of Android Update System

By Ivan Kuten

So, how can you update Android on mobile devices? While developing software for Smart TVs and Android-based set-top boxes, we’ve narrowed it down to four ways, discarding some very exotic options:

By ProAndroidDev -
Android News
Happy Railway
Happy Railway

By Hadi Lashkari Ghouchani

This post is on the tail of Railway Oriented Programming in Kotlin by Antony Harfield. So you need to read it first and continue here. As it’s obvious I really liked it and tried it out. It needs every process have a result like

By ProAndroidDev -
Android News
Unit Tests and Concurrency
Unit Tests and Concurrency

By Stojan Anastasov

Once Retrofit added RxJava support, RxJava became my go-to concurrency framework for writing Android apps. One of the great things about RxJava is the excellent testing support. It includes TestObserver, TestScheduler, RxJavaPlugins so you can switch your schedulers in tests.

By ProAndroidDev -
Android News
When Compat libraries will not save you
When Compat libraries will not save you

By Danny Preussler

And why you should avoid using the “NewApi” suppression! The idea of “Compat” libraries was probably one of the key aspects of Android dominating the mobile space. Other than with iOS, Android users often could not update their operating system after a new version launch, simply as their phones won’t allow them to, the Android problem of fragmentation.

 

By ProAndroidDev -
droidcon News

Tech Showcases,

Developer Resources &

Partners

/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/home-details/EmployerBrandingHeader
EmployerBrandingHeader
https://jobs.droidcon.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/jobs-droidcon/jobs.droidcon.com
jobs.droidcon.com

Latest Android Jobs

http://www.kotlinweekly.net/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/kotlin-weekly/Kotlin Weekly
Kotlin Weekly

Your weekly dose of Kotlin

https://proandroiddev.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/pad/ProAndroidDev
ProAndroidDev

Android Tech Blogs, Case Studies and Step-by-Step Coding

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
Zalando

Meet one of Berlin's top employers

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
Academy for App Success

Google Play resources tailored for the global droidcon community

Follow us

Team droidcon

Get in touch with us

Write us an Email

 

 

Quicklinks

> Code of Conduct

> Terms and Conditions

> How to hold a conference

> FAQs

> Imprint

Droidcon is a registered trademark of Mobile Seasons GmbH Copyright © 2020. All rights reserved.

powered by Breakpoint One