# Motif
[![Build Status](https://travis-ci.com/uber/motif.svg?branch=master)](https://travis-ci.com/uber/motif)
Motif is a DI library that offers a simple API optimized for nested scopes. Under the hood it generates [Dagger](https://google.github.io/dagger/) code.
## Other Resources
* [Dagger Interoperability](https://github.com/uber/motif/blob/master/DAGGER.md)
* [IDE Integration](https://github.com/uber/motif/tree/master/intellij)
* [DroidCon Talk](https://youtu.be/Y45MqYNjts0)
## Gradle
| [![Maven Central](https://img.shields.io/maven-central/v/com.uber.motif/motif-compiler.svg)](https://search.maven.org/artifact/com.uber.motif/motif-compiler)<br>[![Maven Central](https://img.shields.io/maven-central/v/com.uber.motif/motif.svg)](https://search.maven.org/artifact/com.uber.motif/motif) | <pre>annotationProcessor 'com.uber.motif:motif-compiler:x.y.z'<br>compile 'com.uber.motif:motif:x.y.z'</pre> |
|-|:-|
## The Basics
This is a Motif Scope. It serves as a container for objects that can be created by this Scope:
<details>
<summary>Notes for Dagger users...</summary>
A Motif Scope is analogous to a Dagger `@Component`.
</details>
```java
@motif.Scope
interface MainScope {}
```
Define a `@motif.Objects`-annotated class to hold *factory methods*, which tell Motif how to create objects.
<details>
<summary>Notes for Dagger users...</summary>
The nested `Objects` class is just like a Dagger `@Module` except Motif only allows you to define one `Objects` class per Scope. Factory methods are analogous to `@Provides` methods.
</details>
```java
@motif.Scope
interface MainScope {
@motif.Objects
class Objects {
Controller controller() {
return new Controller();
}
}
}
```
Pass object dependencies as factory method parameters. Motif must know how to create dependencies as well:
```java
@motif.Scope
interface MainScope {
@motif.Objects
class Objects {
View view() {
return new View();
}
Database database() {
return new Database();
}
Controller controller(View view, Database database) {
return new Controller(view, database);
}
}
}
```
Retrieve objects from a Scope via *access methods* defined on your Scope interface:
<details>
<summary>Notes for Dagger users...</summary>
Access methods are analogous to a Dagger `@Component` [provision methods](https://google.github.io/dagger/api/2.14/dagger/Component.html#provision-methods).
</details>
```java
@motif.Scope
interface MainScope {
Controller controller();
@motif.Objects
class Objects {
View view() {
return new View();
}
Database database() {
return new Database();
}
Controller controller(View view, Database database) {
return new Controller(view, database);
}
}
}
```
At build time, Motif generates an implementation class for each scope:
```java
MainScope mainScope = new MainScopeImpl();
Controller controller = mainScope.controller();
```
## Child Scopes
Define a *child method* on the Scope interface to declare a Scope as the child of another Scope:
<details>
<summary>Notes for Dagger users...</summary>
This is similar to a Dagger `@Subcomponent` [factory method](https://google.github.io/dagger/api/2.14/dagger/Component.html#subcomponents) on a parent `@Component`.
</details>
```java
@motif.Scope
interface MainScope {
ChildScope child();
// ...
}
```
Annotate a factory method with `@Expose` to make it visible to child Scopes:
<details>
<summary>Notes for Dagger users...</summary>
Unlike Dagger `@Subcomponents` which expose all objects down the graph by default, Motif Scopes consider objects internal to the Scope unless explicitly annotated otherwise.
</details>
```java
@motif.Scope
interface MainScope {
ChildScope child();
// ...
@motif.Objects
class Objects {
@Expose
Database database() {
return new Database();
}
// ...
}
}
@motif.Scope
interface ChildScope {
ChildController controller();
@motif.Objects
class Objects {
// No Database factory method. Child Controller receives the Database defined by MainScope.
ChildView view() {
return new ChildView();
}
ChildController controller(Database database, ChildView view) {
return new ChildController(database, view);
}
}
}
```
Create an instance of a child Scope by calling the parent's child method:
```java
MainScope mainScope = new MainScopeImpl();
ChildScope childScope = mainScope.child();
```
## Root Scopes
If Motif finds a nested interface annotated with `@Dependencies` on a Scope, it uses that interface to define exactly what this scope needs from its parent. This is required in order for Motif to tell you when you have unsatisfied dependencies. The recommended pattern is to always declare an empty `@Dependencies` interface on root Scopes:
```java
@motif.Scope
interface MainScope {
// ...
@motif.Dependencies
interface Dependencies {}
}
```
With the root `Dependencies` interface in place, Motif will report any missing dependencies at build time. Without it, missing dependencies will still cause the build to fail, but the error messages will be less intuitive.
## Convenience APIs
Factory methods that pass parameters through to a constructor without modification can be converted to parameterless abstract methods:
<details>
<summary>Notes for Dagger users...</summary>
This feature is similar to Dagger's `@Inject` constructor injection, but it only requires annotating the class' constructor if there are multiple constructors, and it scopes the object to the enclosing Motif Scope.
</details>
```java
@motif.Scope
interface MainScope {
// ...
@motif.Objects
abstract class Objects {
abstract View view();
abstract Database database();
abstract Controller controller();
}
}
```
Motif understands inheritence and generics as well:
```java
interface ControllerObjects<C, V> {
V view();
C controller();
}
@motif.Scope
interface MainScope {
// ...
@motif.Objects
abstract class Objects implements ControllerObjects<Controller, View> {
abstract Database database();
}
}
```
## Motif vs Dagger
* Related: [Dagger Interoperability](https://github.com/uber/motif/blob/master/DAGGER.md)
Motif sacrifices flexibility in favor of an opinionated API optimized specifically for deep DI scope hierarchies (ie. many levels of nested `@Components` or `@Subcomponents`). In these cases, Motif aims to minimize initial development cost and continued conceptual overhead attributed to DI configuration by offering a simple, targeted API. Dagger can express everything that Motif can and much more, but Dagger's greater flexibility requires many more concepts to be understood by the developer, increases verbosity, and decreases readability. As a universal library designed to satisfy a wide variety of DI topologies, this is the right trade-off for Dagger. Some applications require that flexibility and in those cases, Motif isn't suitable. Motif will be most effective in codebases that follow or adopt the following patterns:
* Granular scoping
* Deeply nested scopes
* Low intra-scope DI complexity
Below is a comparison between a Dagger and a Motif version of such an example.
#### Dagger ([Full Example](https://github.com/uber/motif/tree/master/samples/dagger-comparison/src/main/java/motif/daggercomparison/dagger))
```java
@RootComponent.Scope
@Component(modules = RootComponent.Module.class)
public interface RootComponent {
RootController controller();
LoggedInComponent.Builder loggedIn();
@dagger.Component.Builder
interface Builder {
@BindsInstance
Builder viewGroup(@Root ViewGroup parent);
RootComponent build();