The development process is a research. Find the state machine, organize mutations, wire it for consumers. The process repeats and leads to the meta-research. We investigate scenarios and search for reusable parts. Patterns arise, implementations follow. Ideally, the code resembles a fractal structure.
An architecture is said to be fractal if subcomponents are structured in the same way as the whole is.
The Problem
Let’s say we have a fractal components structure. Each one has a View
and a ViewModel
.
There is a Movie
component which delegates to sub-components
of the same structure — MovieCast
and MovieRating
. The movie view
contains the cast view and the rating view, the same applies to the view model.
interface MovieView {
val cast: MovieCastView
val rating: MovieRatingView
}
interface MovieViewModel {
val cast: MovieCastViewModel
val rating: MovieRatingViewModel
}
This is a fine structure but IRL classes are separate.
interface MovieView
interface MovieViewModel
interface MovieCastView
interface MovieCastViewModel
interface MovieRatingView
interface MovieRatingViewModel
The reliance on the naming is apparent. It is repeating, verbose and does not show that components follow the same structure. What if…
namespace Movie {
interface View
interface ViewModel
}
namespace MovieCast {
interface View
interface ViewModel
}
namespace MovieRating {
interface View
interface ViewModel
}
Much better. It is clear that components are using the same pattern. It scales!
interface MovieView
interface MovieViewModel
class MovieActivity
vs.
namespace Movie {
interface View
interface ViewModel
class Activity
}
The namespace
keyword is obviously made up, Kotlin does not have it.
How do we achieve the same effect?
The Solution
package
package component
interface View
interface ViewModel
It works but it is awkward to use:
package movie.cast
interface View
package movie
interface View {
val cast: movie.cast.View
}
We are forced to use the FQN
since the compiler is not able to distinguish between View
classes.
Not good.
enum
I’m not actually joking.
enum class Component {
;
interface View
interface ViewModel
}
➕ It is impossible to create the Component
instance.
➖ It is awkward to have a semicolon at the beginning of each component.
➖ Semantics are messed up.
In Java and Kotlin enum
represent variations of the enumeration
(unlike Swift where enum
cases might have completely different structure).
While nested classes are not enum
cases it feels wrong.
object
object Component {
interface View
interface ViewModel
}
It works! The Java representation is not fun though.
public final class Component {
public static final Component INSTANCE;
private Component() {
}
static {
Component var0 = new Component();
INSTANCE = var0;
}
Yikes! We create a completely useless singleton for no reason. Moving on.
class
class Component {
interface View
interface ViewModel
}
What a class
y way to do things. Looks fine in the Java representation.
public final class Component {
Let’s take a deeper look using the disassembler.
$ javap -C Component.java && javap -c -verbose Component.class
Size 194 bytes
MD5 checksum 3a2a9141881597581908528a19f7d993
Compiled from "Component.java"
public final class Component
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
Constant pool:
#1 = Methodref #3.#10 // java/lang/Object."<init>":()V
#2 = Class #11 // Component
#3 = Class #12 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 SourceFile
#9 = Utf8 Component.java
#10 = NameAndType #4:#5 // "<init>":()V
#11 = Utf8 Component
#12 = Utf8 java/lang/Object
{
public Component();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
}
➕ Looks innocent compared to the object
.
➕ Impossible to inherit from since Kotlin classes are final
by default.
➖ Possible to create an object.
interface
interface Component {
interface View
interface ViewModel
}
Almost the same as the class
approach. The Java representation does not bring surprises.
interface Component {
The disassembler shows that it is much lighter than the class
.
$ javap -C Component.java && javap -c -verbose Component.class
Size 101 bytes
MD5 checksum 574e4f61d4b3e17ccd964289678c7ae2
Compiled from "Component.java"
interface Component
minor version: 0
major version: 52
flags: ACC_INTERFACE, ACC_ABSTRACT
Constant pool:
#1 = Class #5 // Component
#2 = Class #6 // java/lang/Object
#3 = Utf8 SourceFile
#4 = Utf8 Component.java
#5 = Utf8 Component
#6 = Utf8 java/lang/Object
{
}
➕ Impossible to create an object.
➕ Lighter than the class
— 6 constants in the pool vs. 12, 101 bytes on disk vs. 194.
➖ Possible to inherit from.
namespace
?
namespace Component {
interface IView
interface IViewModel
}
The I
prefix means that we are in the C# world.
It might be surprising but there are not a lot of languages supporting namespaces.
I can name C++, C#, PHP, TypeScript and that’s basically it. In fact,
languages with the namespace
keyword do not have a concept of packages.
Seems like namespaces can cover everything packages provide
but I wonder — why not have both?
The Decision
There is no good way to put it — all approaches are not great.
The reason is simple — Kotlin drags a ton of JVM baggage and
maintains Java compatibility. Approaches described above are
basically the direct translation from the Java world (except the object
variant).
However, namespacing can be achieved using nested interface
declarations.
It is the most efficient approach so far. It would be great to have
the namespace
keyword though. I imagine it being like an interface
which cannot be inherited. Let’s KEEP
this in mind. For now — interface
it up!