# Reduce Complexity with SOLID Principles

[Iveta Jurcikova](https://www.strv.com/blog/authors/iveta)Android Engineer

---

Hey Android developers! Are you excited about Jetpack compose? I bet you are! And what about a talk covering SOLID principles? Not so cool, huh?

Wait, wait, don’t close the tab. Let me speak about just one of them. It’s not going to be theoretical or boring, I promise️! I recently bumped into SOLID again and I’d like to show you a hidden power, which I had not noticed before.

Let’s take one more step into the past and create a Gradle project from scratch, written in Java (sorry, Java). No additional build configuration is applied. The structure of the project is shown in the following diagram.

The Gradle version, at the time of writing this article, is **v6.8.3**. The [incremental compilation feature](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.CompileOptions.html?ref=strv.ghost.io#org.gradle.api.tasks.compile.CompileOptions:incremental) is enabled by default with the Java plugin.

Imagine that `Client1` only uses method `m1`, `Client2` only uses method `m2`, and `Client3` only uses method `m3` of `Service`. What do you think will happen if we change the body of `m3` and recompile the project?

You may think that once a change to the body of a method is a [binary compatible change](https://docs.oracle.com/javase/specs/jls/se7/html/jls-13.html?ref=strv.ghost.io#jls-13.4.22), only `Service` would be recompiled. I get it... but you are wrong. The reality is, the whole project recompiles.

Those who got it right have gained my respect. You may close the tab now. :)

To see the result directly from Gradle, we take advantage of the [listFiles](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.CompileOptions.html?ref=strv.ghost.io#org.gradle.api.tasks.compile.CompileOptions:listFiles) option for Java compilation that logs the files to be compiled.

```gradle
tasks.withType(JavaCompile) {
    options.listFiles = true
}
```

We run a compilation of Java source files using the JDK compiler by calling the [compileJava](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html?ref=strv.ghost.io) task that is normally executed with every `build` or `assemble` task.

```bash
./gradlew compileJava
```

Finally, we can see that **all source files got recompiled**!

> Task :compileJava  
> Source files to be compiled:  
> /ISP_java/src/main/java/com/jurcik/ivet/service/Service.java  
> /ISP_java/src/main/java/com/jurcik/ivet/client/Client1.java  
> /ISP_java/src/main/java/com/jurcik/ivet/client/Client2.java  
> /ISP_java/src/main/java/com/jurcik/ivet/client/Client3.java

**Why is it like that?**

There is nothing wrong with Gradle. The result we got is expected Java behavior. The [binary compatibility](https://docs.oracle.com/javase/specs/jls/se7/html/jls-13.html?ref=strv.ghost.io) has no impact on the list of the compiled source files.

All declarations that users must `import` in source code create a source code dependency. The source code of `Client1` then depends on methods `m2` and `m3`, even though it does not call them. This dependence means that a change to the source code of `m2` or `m3` in `Service` will trigger a recompilation of `Client1`, despite the fact that it does not care about the change.

## The Interface Segregation Principle to the Rescue

The [Interface Segregation Principle](https://en.wikipedia.org/wiki/Interface_segregation_principle?ref=strv.ghost.io) (ISP) states that **no client should be forced to depend on methods it does not use.**

Let’s follow it by creating a separate interface for each method. Every interface exposes to a particular `Client` only what the client really needs. The structure is illustrated in the following diagram.

Now the same question, once again. What do you think will happen if we change the body of the `m3` method of `Service` class, which is a [binary compatible change](https://docs.oracle.com/javase/specs/jls/se7/html/jls-13.html?ref=strv.ghost.io#jls-13.4.22), and recompile the project?

```bash
./gradlew compileJava
```

Voila!

> Task :compileJava  
> Source files to be compiled:  
> /ISP_java/src/main/java/com/jurcik/ivet/service/Service.java

Only `Service` got recompiled, thanks to introducing interfaces!

**What about Kotlin?**

Kotlin compiler is smart enough to only recompile affected files by tracking **Application Binary Interface** changes (“API” for the binaries — you can read more about the process [here](https://blog.jetbrains.com/kotlin/2020/09/the-dark-secrets-of-fast-compilation-for-kotlin/?ref=strv.ghost.io)). However, this does not make the principle obsolete! The compiler just makes it a bit easier for you. Anyway, it is good to know that extra work has to be done.

## Conclusion

Every developer familiar with [OOP](https://en.wikipedia.org/wiki/Object-oriented_programming?ref=strv.ghost.io) should have heard about SOLID principles. We follow them on a daily basis, many times without noticing. Even though we all have them rooted deep down in our minds, it is worth giving them special attention. **SOLID principles matter.** And their boundaries do not end with a system’s architecture.

## Resources

- Martin, R.C. (2018). ‘ISP: The Interface Segregation Principle’, in Martin R.C. (ed.) *Clean Architecture.* Pearson Education, Inc., pp. 83–86.
- [Andrey Breslav](https://blog.jetbrains.com/author/andrey-breslav?ref=strv.ghost.io). [The Dark Secrets of Fast Compilation for Kotlin](https://blog.jetbrains.com/kotlin/2020/09/the-dark-secrets-of-fast-compilation-for-kotlin/?ref=strv.ghost.io).

*Thank you to Alexander Kovalenko for adding a spark to the article and Tomas Mlynaric with Lubos Mudrak for consultations.*

---

Don't miss anything