3 Keeping objects consistent

 

This chapter covers

  • Keeping classes consistent
  • Modeling aggregates that hold complex object relationships
  • Implementing validation mechanisms that ensure consistency at all times

A well-designed class encapsulates its data and provides operations to access or manipulate it. These operations ensure that the object remains in a valid state without inconsistencies. Better yet, they do so in a way that the clients of the class don’t even need to know about it.

One of the greatest advantages of object-oriented programming is the ability to ensure that objects are always in a consistent state. Compare it with, say, procedural programming languages like C. In C, you can define data structures (known as structs). However, there’s no way to control who changes the values inside the structs. Any piece of code, anywhere in the codebase, can change them.

When code is not appropriately encapsulated, developers feel they can never find where to patch the code or fully fix a bug in one shot. When code is spread out and not encapsulated, developers have to constantly search for things. Just going to the class that defines the abstraction isn’t enough. A developer may find a place to fix the code, only to have the same bug appear somewhere else.

3.1 Ensure consistency at all times

3.1.1 Make the class responsible for its consistency

3.1.2 Encapsulate entire actions and complex consistency checks

3.1.3 Example: The Employee entity

3.2 Design effective data validation mechanisms

3.2.1 Make preconditions explicit

3.2.2 Create validation components

3.2.3 Use nulls carefully or avoid them if you can

3.2.4 Example: Adding an employee to a training offering

3.3 Encapsulate state checks

3.3.1 Tell, don’t ask

3.3.2 Example: Available spots in an offering

3.4 Provide only getters and setters that matter