17

GitHub - uber/motif: A simple DI API for Android / Java

 5 years ago
source link: https://github.com/uber/motif
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

README.md

Motif

Build Status

Motif is a DI library that offers a simple API optimized for nested scopes. Under the hood it generates Dagger code.

IMPORTANT: Motif is under heavy development. There will likely be breaking changes.

Gradle

Maven Central
Maven Central
annotationProcessor 'com.uber.motif:motif-compiler:x.y.z'
compile 'com.uber.motif:motif:x.y.z'

Features

The Basics

This is a Motif Scope. It serves as a container for objects that can be created by this Scope:

Notes for Dagger users...

A Motif Scope is analogous to a Dagger @Component.

@motif.Scope
interface MainScope {}

A class annotated with @motif.Objects class holds factory methods, which tell Motif how to create objects.

Notes for Dagger users...

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.

@motif.Scope
interface MainScope {

    @motif.Objects
    class Objects {

        Controller controller() {
            return new Controller();
        }
    }
}

Object dependencies can be passed into factory methods as parameters as long as Motif knows how to instantiate them as well:

@motif.Scope
interface MainScope {

    @motif.Objects
    class Objects {

        View view() {
            return new View();
        }
        
        Database database() {
            return new Database();
        }

        Constroller controller(View view, Database database) {
            return new Controller(view, database);
        }
    }
}

To retrieve objects from your Scope, define an access method on your Scope interface

Notes for Dagger users...

Access methods are analogous to a Dagger @Component provision methods.

@motif.Scope
interface MainScope {

    Controller controller();

    @motif.Objects
    class Objects {

        View view() {
            return new View();
        }
        
        Database database() {
            return new Database();
        }

        Constroller controller(View view, Database database) {
            return new Controller(view, database);
        }
    }
}

At build time, Motif generates an implementation class for each scope:

MainScope mainScope = new MainScopeImpl();
Controller controller = mainScope.controller();

Child Scopes

Motif allows you to define a Scope as a child of another Scope by declaring a child method on the parent Scope interface:

Notes for Dagger users...

This is similar to a Dagger @Subcomponent factory method on a parent @Component.

@motif.Scope
interface MainScope {

    ChildScope child();
    
    // ...
}

Child Scopes can use objects provided by parent factory methods as long as they are annotated with @Expose:

Notes for Dagger users...

Unlike Dagger @Subcomponents which expose all objects down the graph by default, Motif Scopes consider objects internal to the Scope unless explicitly annotated otherwise.

@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.

        ChildView view() {
            return new ChildView();
        }

        ChildController controller(Database database, ChildView view) {
            return new ChildController(database, view);
        }
    }
}

You can create an instance of a child Scope by calling the parent's child method:

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:

@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:

Notes for Dagger users...

This feature is similar to Dagger's @Inject constructor injection, but it doesn't require annotating the class' constructor, and it scopes the object to the enclosing Motif Scope.

@motif.Scope
interface MainScope {

    // ...

    @motif.Objects
    abstract class Objects {
        abstract View view();
        abstract Database database();
        abstract Constroller controller();
    }
}

Motif understands inheritence and generics as well:

interface ControllerObjects<C, V> {
    V view();
    C controller();
}

@motif.Scope
interface MainScope {

    // ...

    @motif.Objects
    abstract class Objects implements ControllerObjects<Controller, View> {
        abstract Database database();
    }
}

Snapshots

Snapshots of the development version are available in Sonatype's snapshots repository.

License

 Copyright (c) 2018 Uber Technologies, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK