3

When debugging object oriented (Java) code, sometimes some field of some object is null while it should not be. I often spend what I feel is way too much time trying to figure out where it should have been set (at which point it's often quickly clear what went wrong).

If it had been a function argument, I'd be easy to trace where it came from by setting a breakpoint and following the stacktrace.

But if I have some ThingFunctions class which gets a ThingList from OtherThing and the Thing inside the list is missing it's id, I usually end up checking all usages of Thing.id, ThingList.add, OtherThing.setThingList and more just to find out who was responsible for setting the id.

Breakpoint don't help much since they only tell me where something happens, not where something doesn't happen.

This takes orders of magnitude more time than following a stacktrace to find why an argument is null. Is there an equally easy solution for debugging this problem? Any general advice? I have a modern IDE, other tools are welcome too.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
Mark
  • 662
  • 5
  • 12
  • 3
    There's no silver bullet for your problem, it is quite common in Object Oriented programming, and making it better requires some pretty big re-writing. – Graham Sep 14 '17 at 17:05
  • General advice: use getters and setters instead of directly accessing fields... that way you can use your IDE tools to track down all locations where it is set rather than also where is it used. – Maybe_Factor Sep 15 '17 at 02:15

4 Answers4

11

There are two ways to deal with object state:

  1. Have extremely sophisticated debugging skills so that you can find obscure bugs, or
  2. Manage your state in a better way so that obscure bugs do not happen in the first place.

Ways to better manage object state:

  1. Use constructors to build objects whose state is already valid.
  2. Make your state private.
  3. Write setter methods that validate changes in state. Block those state changes that are invalid.
  4. Use immutable objects when appropriate.
Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • 1
    "obscure bugs do not" -> "obscure bugs cannot"? – Caleth Sep 14 '17 at 16:04
  • 1
    They can. Better state management only reduces the risk; it does not eliminate it. – Robert Harvey Sep 14 '17 at 16:05
  • I would add, set or change each piece of state in as few places as possible. And keep your call chain from higher to lower level objects, avoiding calls from lower level to higher level, as typically occurs in callbacks. – Frank Hileman Sep 14 '17 at 23:25
  • This is a good answer, but it would be nice if you would elaborate on the 4 points, perhaps with some examples. – user1118321 Sep 15 '17 at 05:07
3

One potential solution is to make sure that your object's field is always set in the constructor. Often, that will mean that the constructor takes the value of this field as an argument; it's much harder to forget to pass an argument to a constructor than it is to forget to set a field after the fact.

A potential downside is that you can't create the object until you know what the value of this field should be. Sometimes (usually?), the solution to this is pretty straightforward: just don't create the object until you know what the value of the field should be.

Tanner Swett
  • 1,359
  • 8
  • 15
1

The most general advice is: make the atribute private.

Your object should be responsible of it state and not depend of others to be in a valid state. The API of the class should offer behavior not data manipulation.

0

This is a rather broad question, and the general answer is "use good architecture and design". Debugging will not help you, but designing your code correctly will.

  • Design your application according to SOLID principles.
  • Use immutable data structures.
  • Enforce pre- and postconditions (i.e. fail fast); always construct your object into a valid state or crash otherwise.
  • Don't allow null references across your public interfaces (within a class is ok if you can't avoid them in your programming language).
  • Use value types instead of primitives (see primitive obsession).
  • Use techniques from domain driven design.

You are absolutely right that nulls are problematic and that's why its inventor called it his "billion dollar mistake".

My bullet points are rather generic, but you can google and read up on each topic. You will then understand how you can become better at avoiding the problem.

Congratulations! Many programmers don't even realize that this is a problem and continue to write costly buggy code.

jhyot
  • 429
  • 3
  • 12