Fork me on GitHub

Variable Tutorial

This tutorial demonstrates passing values downstream with variables.

Unlike function/method/procedures calls, variables do not need to be sent directly by the caller. Variables can be set upstream and dependency injected anywhere downstream. This allows a lot more flexibility to composing flows together.

Tutorial Source

Out<T> / Var<T>

The following method demonstrates setting values for variables.

public class OutLogic {

	@Next("use")
	public static void setValues(Out<Person> person, @Description Out<String> description) {
		person.set(new Person("Daniel", "Sagenschneider"));
		description.set("Need to watch his code!");
	}
}

Variables will be created by reflectively interrogating the parameters. As many variables may be of the same type, qualifiers can be used to distinguish them (just like naming a variable).

The Qualified annotation can be used for text names. However, this is discouraged, as does not refactor well. Using Qualifier on annotations is the recommended approach to naming variables.

The qualifier is as follows.

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Description {
}

Typically, the Out<T> should always be used to set the variable. This allows reasoning when compiling flows for out/in combinations to avoid uninitialised variables (i.e. null values).

However, there are times when you both need to get and set the variable value. This can be achieved with the Var<T>. However, please note that this is discouraged, as reduces ability for reasoning of flow composition (i.e. whether out/in combinations line up).

public class VarLogic {

	@Next("use")
	public static void setValues(Var<Person> person, @Description Var<String> description) {
		person.set(new Person("Daniel", "Sagenschneider"));
		description.set("Need to watch his code!");
	}
}

@Val / In<T>

The following method demonstrates obtaining values for variables.

public class ValLogic {

	public static void useValues(@Val Person person, @Description @Val String description,
			ObjectResponse<ServerResponse> response) {
		response.send(new ServerResponse(person, description));
	}
}

Values for variables can be annotated with @Val. This will distinguish them from other objects to be dependency injected (such as means to send the response).

Typically, for readability and to avoid referencing too many framework classes, the Out<T> to @Val combinations are used.

However, for those that like symmetry there is also the In<T>.

public class InLogic {

	public static void useValues(In<Person> person, @Description In<String> description,
			ObjectResponse<ServerResponse> response) {
		response.send(new ServerResponse(person.get(), description.get()));
	}
}

Composing Flows

The following demonstrates weaving together different combinations of the above.

VariableHttpServer screen shot.

Testing

As all dependencies are injected directly into the methods, it is very easy to unit test the methods.

	@Test
	public void setValues() {
		MockVar<Person> person = new MockVar<>();
		MockVar<String> description = new MockVar<>();

		OutLogic.setValues(person, description);

		assertEquals("Daniel", person.get().getFirstName(), "Incorrect first name");
		assertEquals("Sagenschneider", person.get().getLastName(), "Incorrect second name");
		assertEquals("Need to watch his code!", description.get(), "Incorrect description");
	}

	@Test
	public void sendValues() {
		MockObjectResponse<ServerResponse> response = new MockObjectResponse<>();

		ValLogic.useValues(new Person("Daniel", "Sagenschneider"), "Need to watch his code!", response);

		assertEquals("Daniel", response.getObject().getPerson().getFirstName(), "Incorrect first name");
		assertEquals("Sagenschneider", response.getObject().getPerson().getLastName(), "Incorrect second name");
		assertEquals("Need to watch his code!", response.getObject().getDescription(), "Incorrect description");
	}

Once the methods are confirmed to pass unit testing, OfficeFloor also makes it easy to test the composed flows.

	@RegisterExtension
	public MockWoofServerExtension server = new MockWoofServerExtension();

	@Test
	public void outIn() throws Exception {
		MockWoofResponse response = this.server.send(MockWoofServer.mockRequest("/outIn"));
		response.assertJson(200, new ServerResponse(new Person("Daniel", "Sagenschneider"), "Need to watch his code!"));
	}

	@Test
	public void varVal() throws Exception {
		MockWoofResponse response = this.server.send(MockWoofServer.mockRequest("/varVal"));
		response.assertJson(200, new ServerResponse(new Person("Daniel", "Sagenschneider"), "Need to watch his code!"));
	}

	@Test
	public void outVal() throws Exception {
		MockWoofResponse response = this.server.send(MockWoofServer.mockRequest("/outVal"));
		response.assertJson(200, new ServerResponse(new Person("Daniel", "Sagenschneider"), "Need to watch his code!"));
	}

Next

The next tutorial covers injecting customised managed objects.