It’s very hard to make objects declarative. Scalar Driven Development can help you to model your objects as declarative parts of application.
First things first. Imperative programming covers 99% of code. We’re still writing the same procedural code, that we did in C 50 years ago. We are done with it. It’s time to cure our brains.
Scalars are Declarative
Scalar Driven Development is a way to model our objects as declarative parts, some results, that will be computed later.
We define the interface first, where we denote our Scalar object.
Here is an example:
interface User extends Scalar<Json>{
@Override
Json value();
}
Our Scalar from User is a JSON document.
By the way, Scalar<T>
taken from cactoos library.
Then we are creating a few implementations for it:
final class Simple implements User {
private final String username;
@Override
public Json value() {
return new MutableJson().with("urn", this.username);
}
}
No big deal, just giving username and building a JSON document from it. Someday, the logic will scale or change completely. But we want our object to stay the same, while adding new features. The Decorator will be in charge:
final class Validated implements User {
private final Scalar<Json> user;
@Override
public Json value() {
// validate json
return this.user.value();
}
}
Now we can validate our username before applying it to JSON document storage. Frankly speaking, it’s not a plain decorator, we are chaining a scalar by passing it.
final class Flushed implements User {
private final Scalar<Json> user;
private final Documents docs;
@Override
public Json value() {
this.docs.apply(this.user.value());
}
}
That’s a composition of objects, where each of them is a good citizen of a pipeline. Each of them is represented as a small result, as a part of a bigger one.
Delayed calls
Nothing will be computed until value()
is called:
new Flushed(
new Validated(
new Simple(
"Aliaksei"
)
),
docs
).value(); // trigger pipeline
Scalars vs. Workers
XmlParser
, BookMapper
, UserValidator
.
Who are they?!
I think they are great candidates for refactoring…
Scalars also will help you to avoid evil
suffix of your objects: -ER
,
a.k.a Worker, we just need to define our results of manipulations with objects
as other objects, that’s how Scalar is born.
“Declarative programming means you define results of manipulations with objects as other objects”.
Let’s see an example: we need to map HttpRequest
to Song
object:
interface Mapper<F, T> {
T to(F from);
}
final class SongMapper implements Mapper<HttpRequest, Song> {
@Override
public Song to(final HttpRequest rq) {
return new Song(rq.path("name"));
}
}
// call it
final SongMapper mapper = new SongMapper();
final Song song = mapper.to(request);
SongMapper
encapsulates nothing, frankly speaking
it’s not even a class, it’s nothing more than a set of procedures.
More than that, SongMapper
represent nothing from a real-world.
We are just treating it like some smart object to manage our dumb DTOs.
Instead of doing such procedural things, we can go with SDD; it will make our objects more declarative:
final class SongFromHttp implements Scalar<Song> {
private final HttpRequest request;
@Override
public Song value() {
return new Song(this.request.path("name"));
}
}
// call it
final Song song = new SongFromHttp(request).value();
The same functionality, but now:
- we are treating
SongFromHttp
as real-world entity. SongFromHttp
has a state inside self: object encapsulatesHttpRequest
- thanks to 2., we can perform lazy computation of
SongFromHttp#value()
Watch this:
Summary
What are we learning here? Here is my summary of what SDD means:
- State (your object must encapsulate something)
- Declarative (scalars are treated as parts of other objects, as some results)
- Delayed calls (unless
value()
is called, nothing will happen) - Absence of getters (there is
value()
, no sense to violate encapsulation)
There are a few real-world examples of how we maintain those ‘scalars’ in our projects: eo-kafka, cmig, cactoos and many more at EO-CQRS.
That’s it.
P.S
@l3r8yJ called this way of development - Scalar Driven Development (SDD).