/jcomposition

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

Primary LanguageJavaApache License 2.0Apache-2.0

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;
    }
    @Module
    public static final class MovableModule {
        @Provides
        public Movable provideMovable() {
            return new 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 onInject(Composition composition) {
        injectionComponent = DaggerInjectionComponent
                .builder()
                .movableModule(new Movable.MovableModule())
                .build();

        injectionComponent.inject(composition);
    }
}

Download

TODO