bubbles
bubbles

Reputation: 2727

RxJavaPlugins, the pattern behind

I want to reproduce the "static" injection of RxJava using the RxJavaPlugins and i'am wondring if the design of this class is a well known pattern !?

I have a basic model class for all my events

import lombok.Getter;

public abstract class BasicEvent {

    @Getter
    private final String id;

    @Getter
    private final long timestamp;

    public BasicEvent() {
        this.id = IDGenerator.BY_COUNTER.generate();
        this.timestamp = TimePlayer.clock().timestamp();
    }
}

The main drawback of the class above is the testability. I cannot change the IDGenerator and the TimePlayer.

I think that having a third party class, designed like RxJavaPlugins could help me changing my metadata generators for tests

UPDATE .

I've followed the RxJavaPlugins solution for keeping model's creation simple, without worrying about the id and the timestamp (i called the two fields metadata), here is solution a found.

public BasicEvent() {
    this.id = MetaGenerator.idGenerator().generate();
    this.timestamp = MetaGenerator.timePlayer().timestamp();
}


public class MetaGenerator {

    public static IDGenerator idGenerator() {
        // DGenerator.BY_COUNTER is the default value
        return MetaGeneratorPlugins.onIDGenerator(IDGenerator.BY_COUNTER);
    }

    public static TimePlayer timePlayer() {
        return MetaGeneratorPlugins.onTimePlayer(TimePlayer.realtime);
    }
}

public class MetaGeneratorPlugins {

    private static Function<? super TimePlayer, ? extends TimePlayer> onTimePlayerHandler;
    private static Function<? super IDGenerator, ? extends IDGenerator> onIDGeneratorHandler;

    public static void setTimePlayerHandler(@Nullable Function<? super TimePlayer, ? extends TimePlayer> handler) {
        onTimePlayerHandler = handler;
    }

    public static void setIDGeneratorHandler(@Nullable Function<? super IDGenerator, ? extends IDGenerator> handler) {
        onIDGeneratorHandler = handler;
    }

    //return the default value or the value returned by the handler
    public static TimePlayer onTimePlayer(@Nonnull TimePlayer defaultScheduler) {
        if (onTimePlayerHandler == null) {
            return defaultScheduler;
        }
        return apply(onTimePlayerHandler, defaultScheduler);
    }

    public static IDGenerator onIDGenerator(@Nonnull IDGenerator defaultScheduler) {
        if (onIDGeneratorHandler == null) {
            return defaultScheduler;
        }
        return apply(onIDGeneratorHandler, defaultScheduler);
    }

    private static <T, R> R apply(@Nonnull Function<T, R> f, @Nonnull T t) {
        return f.apply(t);
    }
}

In my tests now i can change this MetaGenerator's implementation by calling MetaGeneratorPlugins

MetaGeneratorPlugins.setIDGeneratorHandler(IDGenerator.INCREMENTAL);
MetaGeneratorPlugins.setTimePlayerHandler(TimePlayer.simulated);

This is how RxJavaPlugins works, i have been inspired by they solution,

Now the creation of my models are simple and it works for production and tests.

Upvotes: 0

Views: 140

Answers (1)

Bob Dalgleish
Bob Dalgleish

Reputation: 8227

Change the design of BasicEvent so that the id and timestamp are bound in a more testable fashion.

Introduce a "stamper" interface:

public interface Stamper {
  String generateId();
  long   generateTimestamp();
}

The "default" implementation would look like:

public RealTimeStamper implements Stamper {
  @Override public String generateId() {
    return IDGenerator.BY_COUNTER.generate();
  }
  @Override public long generateTimestamp() {
    return TimePlayer.clock().timestamp();
  }
}

Provide a testable implementation of Stamper. Then bind the appropriate implementation using whatever dependency injection process you use.

public abstract class BasicEvent {
  static Stamper stamper;
  ...
  public BasicEvent() {
    this.id = stamper.generateId();
    this.timestamp = stamper.generateTimestamp();
  }
}

The stamper field can be injected either manually or using a dependency injection framework.

Upvotes: 2

Related Questions