Visitor - aegisql/conveyor GitHub Wiki
- Visitor
- Creating abstract builder
- Creating smart labels working with this abstract class methods
- Create concrete implementations for two different builders.
- Creating concrete implementations for User class
- Create conveyor without default builder supplier
- Use Create build family methods with explicit builder supplier
Primary goal of the Reactive Builder project was to provide, well, Asynchronous Builder for some complex data structures. In this article we'll show that Reactive builder is actually closely related to the Visitor pattern.
Visitor pattern is often used when you need to provide different actions on some complex data structure. Developer of the structure does not know what kind of actions will be required.
Let's extend already familiar User and UserBuilder, and show how we can dynamically provide different processing for same data.
Imagine that we have two services that need the same User information. However, first server needs First name first, and Last name second, all in lower case, while the second one needs Last name first, and all in upper case. Our datasources keep user info as is.
/*
* COPYRIGHT (C) AEGIS DATA SOLUTIONS, LLC, 2015
*/
package com.aegisql.conveyor.user;
import java.util.function.Supplier;
public abstract class AbstractSmartUserBuilder implements Supplier<User> {
String first;
String last;
Integer yearOfBirth;
private boolean ready = false;
public Integer getYearOfBirth() {
return yearOfBirth;
}
public static void setYearOfBirth(AbstractSmartUserBuilder builder, Integer yob) {
builder.yearOfBirth = yob;
}
public String getFirst() {
return first;
}
public abstract void acceptFirst(String first);
public static void setFirst(AbstractSmartUserBuilder builder, String first) {
builder.acceptFirst(first);
}
public String getLast() {
return last;
}
public abstract void acceptLast(String last);
public static void setLast(AbstractSmartUserBuilder builder, String last) {
builder.acceptLast(last);
}
public boolean ready() {
return ready;
}
public void setReady(boolean ready) {
this.ready = ready;
}
}
/*
* COPYRIGHT (C) AEGIS DATA SOLUTIONS, LLC, 2015
*/
package com.aegisql.conveyor.user;
import java.util.function.BiConsumer;
import com.aegisql.conveyor.SmartLabel;
public enum BuilderEvents implements SmartLabel<AbstractSmartUserBuilder> {
SET_FIRST(AbstractSmartUserBuilder::setFirst),
SET_LAST(AbstractSmartUserBuilder::setLast),
SET_YEAR(AbstractSmartUserBuilder::setYearOfBirth)
;
BiConsumer<AbstractSmartUserBuilder, Object> setter;
<T> AbstractBuilderEvents(BiConsumer<AbstractSmartUserBuilder,T> setter) {
this.setter = (BiConsumer<AbstractSmartUserBuilder, Object>) setter;
}
@Override
public BiConsumer<AbstractSmartUserBuilder, Object> get() {
return setter;
}
}
package com.aegisql.conveyor.user;
public class LowerCaseUserBuilder extends AbstractSmartUserBuilder {
@Override
public User get() {
return new FirstLastUser(first, last, yearOfBirth);
}
@Override
public void acceptFirst(String first) {
this.first = first.toLowerCase();
}
@Override
public void acceptLast(String last) {
this.last = last.toLowerCase();
}
}
public class UpperCaseUserBuilder extends AbstractSmartUserBuilder {
@Override
public User get() {
return new LastFirstUser(first, last, yearOfBirth);
}
@Override
public void acceptFirst(String first) {
this.first = first.toUpperCase();
}
@Override
public void acceptLast(String last) {
this.last = last.toUpperCase();
}
}
public class FirstLastUser extends User {
public FirstLastUser (String first, String last, int yob) {
super(first, last, yob);
}
@Override
public String toString() {
return first + " " + last;
}
}
public class LastFirstUser extends User {
public LastFirstUser (String first, String last, int yob) {
super(first, last, yob);
}
@Override
public String toString() {
return last + " " + first;
}
}
AssemblingConveyor<Integer, BuilderEvents, User> conveyor = new AssemblingConveyor<>();
conveyor.setResultConsumer(res -> {
System.out.println(res.product);
});
conveyor.setReadinessEvaluator((state, builder) -> {
return state.previouslyAccepted == 3;
});
conveyor.setName("Visiting Assembler");
conveyor.setIdleHeartBeat(100, TimeUnit.MILLISECONDS);
conveyor.setDefaultBuilderTimeout(100, TimeUnit.MILLISECONDS);
CompletableFuture<User> lowerCaseFuture = conveyor.createBuildFuture(1,LowerCaseUserBuilder::new);
CompletableFuture<User> upperCaseFuture = conveyor.createBuildFuture(2,UpperCaseUserBuilder::new);
//then, as usually, send data
conveyor.offer(1, "John", BuilderEvents.SET_FIRST);
conveyor.offer(1, "Doe", BuilderEvents.SET_LAST);
conveyor.offer(1, 1999, BuilderEvents.SET_YEAR);
conveyor.offer(2, "John", BuilderEvents.SET_FIRST);
conveyor.offer(2, "Doe", BuilderEvents.SET_LAST);
conveyor.offer(2, 1999, BuilderEvents.SET_YEAR);
User user1 = lowerCaseFuture.get();
User user2 = upperCaseFuture.get();
System.out.println(user1);
System.out.println(user2);
As expected, output for user1 and user2 will be different, because their fields were processed differently, and they also belong to different classes. Method get() of the Supplier interface in this case becomes a Factory Method
john doe
DOE JOHN