Incredibles 101
Incredibles make your code more readable and less error prone by making it easy to create structs (a.k.a. value objects), and strong types that you can use to wrap even the most trivial primitives.
Incredibles automagically create the value object or the strong type implementation for you, based on the interface alone.
Strong
Why Bother With Strong Typing?
Our experience is that strongly typed code is easier to understand. By improving readability and reducing ambiguity, it reduces the likelihood of bugs. When expensive programmers (you) make less mistakes, it saves money.
For example, you could have an object called Sink. It has a capacity of 30,000 millilitres. You have the option of making a weakly typed method (eeeek ptoooie, blaaaah) like this…
sink.fill(20000);
…which puts 20 litres of water in the sink. When you are reading this code, you have to go into the method (then maybe deeper, and deeper into other classes perhaps depending on how well the method parameters are named) to ascertain the code wants millilitres and not litres.
But with strong typing, you have…
Millilitres m = new Millilitres(20000); sink.fill(m);
You know from the interface (yummm, interface - good) what you are dealing with straight up, no ferreting about. For us, the argument for strong typing is convincing enough just talking about the improved readability of code that “sets” values like this. Real bugs occur when we “get” untyped values in our code and use them.
So, to continue on with sinks, and because I am writing this from one of the driest continents on the planet, let’s charge $0.5 for each litre of water someone uses in their sink and see what could happen if we rely on primitives to encapsulate values…
int litres = sink.howMuchWaterHaveYouGotMyDarling(); // <-- eeek this actually returns millilitres int totalCost = litres * 0.5;
This means you are dealing with $10,000 instead of $10 to fill your sink. See, strong typing actually saves you money, as well as making your code more readable!
Now that you are convinced about using strong types, lets have a look at the Incredibles Strong type.
Strong Examples
Here is a simple example of an Incredibles Strong type…
public interface Host extends Strong<String> {
}
You instantiate a Host like this…
nu.nu(Host.class, "boostalicious.org");
Incredibles creates an implementation of a strong type that will contain the String value “boostalicious.org” for you. The idea is that you pass around this strong type in your code in order to reduce confusion about what the String represents.
You can create Strong types for any primitive Java type.
To “unwrap” a Strong, you use an object called Weaken, like this…
public class DefaultHttpClient implements HttpClient {
Weaken weak;
public void connect(Host h) {
String host = weak.w(h);
client.connect(host);
}
}
This unwrapping normally occurs at the boundaries, or edges, of your system. Using strong types in this way helps you create an internal domain model is tight and consistent.
Struct
Structs or value objects often contain boilerplate code in the constructor, and in the getters. Although modern IDEs can automatically generate a lot of this code for you, it is entirely unnecessary. Incredibles Struct types only require an interface definition.
Here is an example…
public interface Sock extends Struct {
String host();
int port();
}
You construct the Sock like this…
nu.nu(Sock.class, "boostalicious.org", 8080)
You use the Sock like this…
public class DefaultHttpClient implements HttpClient {
public void connect(Sock sock) {
String host = sock.host();
int port = sock.port();
client.connect(host, port);
}
}
…sweeeeeet.
Furthermore, you can use Strongs within Structs like this…
public interface Sock extends Struct {
Host host();
int port();
}
…sweeeeeeeet.
Multiple Struct Attributes With the Same Type
There is an issue when there are multiple attributes in the Struct with the same type. To help nu when constructing the Struct, a marker field specifying the argument order needs to be present.
For example…
public interface Person extends Struct {
String firstName();
String lastName();
}
…would result in a Runtime error like this…
Could not infer arguments, for interface Person use explicit '_' field to designate constructor order.
This can be repaired by adding the marker field to indicate the order of arguments to the constructor, like this…
public interface Person extends Struct {
String[] _ = {"firstName", "lastName"};
String firstName();
String lastName();
}
This basically stems from a limitation in the Java reflection library.
That’s about all you need to know about Strongs and Structs to get you going. Incredibles! Yay!
THE END