Reputation: 2727
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
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