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

 

Keep your interfaces simple

Avoiding implicit connections and learn how extension functions can help you writing good APIs

 

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

https://unsplash.com/photos/xxeAftHHq6E

 

Writing your classes with a good API is hard but important. As the writer is trying to make it easy for the user, we sometimes tend to repeat ourselves by adding convenient methods.

Think about the List interface in Java. To check if there are no elements in the list we could check list.getLength() == 0 or we simply ask for isEmpty().

The 2nd one reads much better. But it also adds a duplication and implicit connection between the two: If the list is empty, it can’t contain any elements! This must be respected by every implementer of the interface!

We can easily think of many other methods with implicit dependencies. Think about how hashCode and equals have a connection. This is stated in the Javadocs:

 

Note that it is generally necessary to override the hashCode method whenever equals is overridden, so as to maintain the general contract for the hashCodemethod, which states that equal objects must have equal hash codes.

 

This is a pitfall that can be difficult to avoid and can lead to issues elsewhere that are difficult to track down and fix. Nowadays we have tools to validate this contract, or even better, to generate the implementation.

 

Another example: feature flags

A lot of developers work with features flags. These enable us to release continuously without the need for long-lived feature branches. Let’s say we have an interface like this:

 

interface AppFeatures {
  fun isEnabled(feature: Feature): Boolean
}

 

When using this I realized I often write code like:

 

if (!isEnabled(Feature.SomeFeature))

 

“not is enabled” does not read nicely though. But I want the reader to understand my code without thinking too much. Therefore my initial thought was to add another method to the interface

 

fun isDisabled(feature: Feature): Boolean

 

But I realized, doing this might break a lot of tests that simply mock the interface:

 

val appFeatures = mock<AppFeatures> {
    on { isEnabled(Features.SomeFeature) } doReturn true
}

 

If I were to change the implementation to use isDisabled, the test would fail. But all I changed was an implementation detail without any actual changes. The problem with this is that, again, both methods have a hidden contract: A feature is either disabled or enabled, never both. Every implementation needs to comply, and that includes mocks, but mocks are generated implementation! Our mocking frameworks don’t know about hidden contracts we might have written (probably hidden in some JavaDoc if the contract isn’t so obvious).

This example also shows us why we should try to avoid mocking and instead use a stub implementation.

Let’s take a step back and ask ourselves if it is wise to add this method in the first place. It’s a convenient solution, but also dangerous as we can’t express the contract between two methods in the tools we have today.

Fortunately, Kotlin has some features that could help us.

 

Partial solution: Default implementations

We could add the method including its implementation directly to the interface:

 

fun isDisabled(feature: Feature) = isEnabled(feature).not()

 

This way the contract is fixed. The implementer just need to add isEnabled

Unfortunately, this will not work in mocking libraries like Mockito Kotlin. The reason is that in bytecode there is no actual implementation, it is still a normal Java-like interface. This means your mock is not gonna have the code of isDisabled and instead will return the default valuefalse and therefore break the contract.

 

Extension functions to the rescue

But we have another tool at our disposal. Instead of polluting our interfaces, we can move these comfort functions and still retain their usefulness. Kotlin’s extension functions are exactly about that:

 

fun AppFeatures.isDisabled(feature: Feature) = 
      isEnabled(feature).not()

 

This has many advantages:

  • It doesn’t need to be implemented by fakes or stubbed out in mocks
  • The interface stays simple and clean
  • The contract is fixed
  • Works with the way we want to use mocks in tests

Extension functions are a very powerful tool. Many developers don’t use them to their full capabilities. So next time you define an API, remember the lesson above.

Edit: Aidan Mcwilliams looked into how you could even use more Kotlin features on this, continue your read here.

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

 

 

 

Tags: Kotlin, Interfaces, Android, Android App Development, AndroidDev

 

View original article at: 


 

Originally published: July 16, 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