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.

  • 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. – Rob Jan 6 '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 6 '15 at 18:23
  • 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.) – Rob Jan 6 '15 at 18:33
  • 2
    You may want to make blankInstance() a static method. – Kevin Jan 6 '15 at 18:40
  • 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 7 '15 at 8:22
2

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.

| improve this answer | |
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.

| improve this answer | |
  • 1
    +1 ...though they are not mutually exclusive. blankInstance() might return a static BLANK. In this context, BLANK is called a "sentinel value". – Rob Jan 6 '15 at 18:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.