3

I was wondering... what is considered the best practice to instantiate blank value objects? (in Java)

E.g. Assume we have some value object class Foo, that could be blank.

Would creating methods like these be a clean solution? (And perhaps adding in a "Blankable" interface or something).

interface Blankable {
    public Foo blankInstance();
    public boolean isBlank();
}

class Foo implements Blankable {
    public Foo blankInstance();
    public boolean isBlank();
}

Foo foo = something();
if (foo.isBlank()) // Tell the user

Or would something more simple like this be better?

class Foo {
    public static Foo BLANK = /* Some constructor */;
}

Foo foo = something();
if (foo == Foo.BLANK) // Tell the user

I am leaning toward the first example, but I am still not sure if it is a widely accepted technique.

I feel a bit resistant to (but still open to) using the Guava Optional< T> class because it seems like a work-around solution in this circumstance.

elimirks
  • 275
  • 2
  • 3
  • 9
  • Use Guava... the Optional class has conceptual links to similar structures in other languages (like Haskell and Scala), and it provides lots of nice methods for values that may not be present. There are good answers to your specific question, but Guava/Optional is a good choice that will actually open some doors in your design. – sea-rob Jan 06 '15 at 18:22
  • @RobY It's possible his type has some notion of emptiness that's not equal to not having a `Foo`. E.g. Strings may be blank, lists and trees may be empty. – Doval Jan 06 '15 at 18:23
  • @Doval sure, but both of those examples have representations as a regular instance, and (in java anyway) the class just provides an "isEmpty()" method. So there's no special class design. Like I say, there *are* good answers to his question (which is why I left mine a comment), but if you lift the "Blankable" concern out of the classes, and sort of shoot forward several lightyears, you end up with `Blankable` with the same semantics as `Optional`. Just providing some insight, though I understand you disagree. – sea-rob Jan 06 '15 at 18:30
  • 2
    Also, just a comment on format, "best practices" is kind of a lightning rod around here, and you might attract some downvotes. You might benefit from rewording the question so you don't use those specific words. (I'll delete this comment if history proves me wrong.) – sea-rob Jan 06 '15 at 18:33
  • @RobY You *could* have `Blankable` but that seems pretty meaningless. What can I do with that knowledge? The useful concept here is probably `Monoid`, a type T that has a value that doesn't change the result of some binary function. E.g. The empty string doesn't change the result of concatenating two strings, 0 doesn't change the result of addition and 1 doesn't change the result of multiplication. But the key thing there is that the identity element is meaningless without the operation (and a type can have more than one monoid, like addition and multiplication). – Doval Jan 06 '15 at 18:39
  • 2
    You may want to make `blankInstance()` a static method. – Kevin Jan 06 '15 at 18:40
  • @Doval `Blankable` can have whatever meaning you assign it. Saying it's meaningless is tantamount to saying that parametric polymorphism has no practical value. – sea-rob Jan 06 '15 at 18:44
  • @RobY After giving it some thought I think the real reason I dismissed it is because a single-method interface is just a function. In Java 8 whatever decision you were planning on doing based on `isBlank()` can just as easily be done by accepting a `Predicate`, and that approach is more general since it still works for classes that don't implement the interface but do have a method with the wanted semantics. – Doval Jan 06 '15 at 18:58
  • 1
    @Doval I disagree. The interface, with its relevant name and context-specific method name make the code more readable. And if you have the interface with only the single "isBlank" method, you can adapt your other code to it using a lambda/instance method reference. Generally, I think (other than for general-purpose higher order functions) interfaces with meaningful names should be preferred over the generic "Predicate", "Function", and their like. – Jules Jan 07 '15 at 08:22
  • @Jules The generic name (e.g. `Predicate`) only shows up in code that by definition can't know what the context is. E.g. `filter(Collection, Predicate)`. Doing something like `filter(Collection)` duplicates the functionality of the former while working in fewer cases, and you'll need to copy it again and again for every other one-method-boolean interface (and there's no limit to how many you can come up with). The context is perfectly clear in the generic version once it's actually *used*, which is the only time the context is available anyways: `filter(foos, Foo::isBlank)`. – Doval Jan 07 '15 at 12:38

2 Answers2

3

A solution such as Option<T> is not a so bad idea. In fact, it has been successfully introduced in Haskell as a Maybe (but I don't know if this is the first time this solution has been used). Maybe monads are common in Haskell programs. Since, other languages have adopted the same system. For instance, Scala has an Option type.

If you don't want to become dependent of Guava, you could use Java 8, which introduces the Optional class.

However, Option/Maybe/… types are more relevant in a functional environment, because what you typically want to do is to get a behaviour/a property from the element if it really exists, and to get nothing or a default behaviour/property otherwise. So, a typical use of Options consists in

  • applying a function A->B on the Option[A] in order to get an Option[B];
  • switching your code on the basis of the real content of the Option (for instance, by using Pattern matching if the language supports it and if Option has two subtypes, or by using method overload);
  • or filtering the Options that represent (non-)existing elements.

As an alternative, you could use the Null Object Pattern, in which you have an abstract class MyClass and two concrete subclasses: MyRealClass and MyNullClass. You manipulate instances of MyClass, but generate instances of MyRealClass (if the element is really existing) or MyNullClass (if the element doesn't exist). MyNullClass contains the default behaviours/properties. If the null objects are stateless (which is typically the case), one could cache them, or even make them singletons.

This pattern is described in [Fowler].

[Fowler] Martin Fowler, Kent Beck, Refactoring: Improving the Design of Existing Code.

mgoeminne
  • 1,158
  • 6
  • 11
  • Interesting.. I am very inclined to follow the ways of Sir Fowler! (By the way: A PDF version of the book may be found here http://www.cs.umss.edu.bo/doc/material/mat_gral_137/M.Fowler%20et%20al%20-%20Refactoring%20-%20Improving%20the%20Design%20of%20Existing.pdf) – elimirks Jan 08 '15 at 19:43
2

The whole point of a value object is that equality isn't based on identity; you could very well have two distinct blank objects. In fact, whether all blank objects are the same or different objects should be an implementation detail. For those reasons, the second approach is not good. If you look at the Java 8 APIs, there's a notion of a "value-based class" with the following properties:

  • are final and immutable (though may contain references to mutable objects);
  • have implementations of equals, hashCode, and toString which are computed solely from the instance's state and not from its identity or the state of any other object or variable;
  • make no use of identity-sensitive operations such as reference equality (==) between instances, identity hash code of instances, or synchronization on an instances's intrinsic lock;
  • are considered equal solely based on equals(), not based on reference equality (==);
  • do not have accessible constructors, but are instead instantiated through factory methods which make no committment as to the identity of returned instances;
  • are freely substitutable when equal, meaning that interchanging any two instances x and y that are equal according to equals() in any computation or method invocation should produce no visible change in behavior.

The documentation further adds:

A program may produce unpredictable results if it attempts to distinguish two references to equal values of a value-based class, whether directly via reference equality or indirectly via an appeal to synchronization, identity hashing, serialization, or any other identity-sensitive mechanism. Use of such identity-sensitive operations on instances of value-based classes may have unpredictable effects and should be avoided.

There's no way to disable the == operator in Java so that warning is the best you can do.

Thus, your first approach is correct, with the caveat that foo1.equals(foo2) should always be true when foo1.isBlank() and foo2.isBlank() even if foo1 != foo2.

Doval
  • 15,347
  • 3
  • 43
  • 58
  • 1
    +1 ...though they are not mutually exclusive. `blankInstance()` might return a static `BLANK`. In this context, `BLANK` is called a "sentinel value". – sea-rob Jan 06 '15 at 18:39