GithubHelp home page GithubHelp logo

trollsoftware / jcomposition Goto Github PK

View Code? Open in Web Editor NEW
10.0 4.0 9.0 179 KB

Composition over inheritance. Lightweight Java/Android API for creating a compositions at compile-time

License: Apache License 2.0

Java 100.00%
java composition inheritance dependency-injection annotation-processor android multiple-inheritance annotations injection library framework auto api diamond mixins mixin-framework traits trait

jcomposition's Introduction

Build Status

JComposition

JComposition is a lightweight Java API based on annotations for creating compositions at compile-time. Composition over inheritance.

Introduction

Say you have 2 interfaces:

public interface IDrawable {
    void draw();
}
public interface IUpdatable {
    void update();
}

And 2 classes that implements them:

public class Drawable implements IDrawable {
    @Override
    public void draw() {
        System.out.println("I'm drawing");
    }
}
public class Updatable implements IUpdatable {
    @Override
    public void update() {
        System.out.println("I'm updating");
    }
}

And perhaps you want a GameObject class that have Drawable and Updatable behaviour:

public class GameObject extends Drawable, Updatable {
}
GameObject gameObject = new GameObject();
gameObject.update();
gameObject.draw();

But Java does not allow you to have more than one superclass. So the code above will not compile.

JComposition is tool that you can use to mix such logic in one class without duplicated code. Also it support dependency injection and generics.

Using JComposition

JComposition uses interfaces as base for code generation.

Basic usage

Define your interfaces for each module you want to extend:

@Bind(Drawable.class)
public interface IDrawable {
    void draw();
}
@Bind(Updatable.class)
public interface IUpdatable {
    void update();
}

@Bind annotation binds your interface to class that implements logic. Implementation of IDrawable and IUpdatable you can find in Introduction section

Then create a interface for GameObject class:

@Composition(name = "GameObjectBase")
@Bind(GameObject.class)
public interface IGameObject extends IUpdatable, IDrawable {
}

@Composition annotation marks this interface for annotation processor to generate composite class with name = "GameObjectBase".

Then just extend GameObject from GameObjectBase:

public class GameObject extends GameObjectBase {
}
// Now this will work
GameObject gameObject = new GameObject();
gameObject.update();
gameObject.draw();

Overriding

By default gameObject.draw() will call getComposition().composition_Drawable.draw(), but you can override behaviour this way:

public class GameObject extends GameObjectBase {
    @Override
    public void draw() {
        super.draw(); // will call getComposition().composition_Drawable.draw()
        
        // Some custom action
        getComposition().composition_Updatable.update();
    }
}
gameObject.draw();
// Output
I'm drawing
I'm updating

Dependency injection

Use @UseInjection annotation to let processor mark composition's fields @Inject annotation.

@Bind(Movable.class)
@UseInjection
public interface IMovable {
    boolean moveTo(int x, int y);
}
public class Movable implements IMovable {
    @Override
    public boolean moveTo(int x, int y) {
        System.out.println("I'm moving to (" + x + ", " + y + ")");
        return false;
    }

    protected abstract void onMove();
}

// And finally @Module declaration
@Module
public final class MovableModule {
    private GameObjectWithInjection.Composition composition;

    public MovableModule(GameObjectWithInjection.Composition composition) {
        this.composition = composition;
    }

    @Provides
    public GameObjectWithInjectionBase.Composition.Composition_Movable provideMovable()  {
        return composition.new Composition_Movable();
    }
}

When Movable composition will ready for injection abstract method onInject(Composition) will be called:

public class GameObjectWithInjection extends GameObjectWithInjectionBase {
    private InjectionComponent injectionComponent;

    @Override
    protected void onMove() {
        System.out.println("OnMove()");
    }

    @Override
    protected void onInject(Composition composition) {
        injectionComponent = DaggerInjectionComponent
                .builder()
                .movableModule(new MovableModule(composition))
                .build();

        injectionComponent.inject(composition);
    }
}

Diamond Problem

JComposition allow you to inherit functionality from many instances, and you could get into a situation that two or more components of composition has some equal methods. Here and below I will call such situation as 'merge conflict'. To solve merge conflict you could use special option in annotation @Composition onConflict, which accept one of classes below or you own, that implements IMergeConflictPolicy:

MakeAbstractPolicy.class
MixVoidPolicy.class
UseFirstPolicy.class

Constraints

If you are not using dependency injection, binded class must have an empty argument constructor.

Examples

You can find more examples here

Download

We are using JitPack for publishing our libraries. Add jitpack.io to your repositories first to build.gradle:

allprojects {
	repositories {
		...
		maven { url 'https://jitpack.io' }
	}
}

And add the dependency:

dependencies {
    // Use compile for processor instead of apt if you haven't apt dependency.
    apt 'com.github.trollsoftware.jcomposition:processor:1.2.1'
    compile 'com.github.trollsoftware.jcomposition:api:1.2.1'
}

Ideas

  1. Custom constructor support
  2. Check how jcomposition works on java 8-9
  3. Inherit java docs in generated files
  4. Add more documentation and examples

License

This library is distributed under the Apache 2.0 license found in the LICENSE file.

jcomposition's People

Contributors

ashitikov avatar rw4lll avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

jcomposition's Issues

`@ShareProtected` annotation

@ShareProtected annotation method now is required to get redefinition of protected method in composition. Does this behaviour should be default without this annotation?

Custom name

Make a name parameter for @Composition annotation.

Back-linking and sharing of protected and public methods

Support this case:

@Bind(Object.class)
interface IObject {
  void run();
  void onRun();
}
class Object implements IObject {
  public void run() {
     onRun();
  } 
  public void onRun() {
   // do some action on Run
  }
}

@Compositition(name = "ChildObjectBase")
interface IChildObject extends IObject, ...{
}
class ChildObject extends ChildObjectBase {
  @Override
  public void onRun() {
     // catch callback
     // now this is impossible because there is no back-linking from composition's component to base class.
  }

  //some fun
  public void main() {
    run();
  }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.