Practice: Encapsulate by Policy, Reveal by Need

Traditionally, encapsulation has been thought of as the hiding of data or “state.” While it is true that hiding data encapsulates it, encapsulation can mean much more than this. It’s like the notion that all dogs are mammals, but not all mammals are dogs.

The better way to think of encapsulation is, “the hiding of anything.” A lot of things can be encapsulated besides data.

Here are examples of things that can be encapsulated.

  • How something is designed
  • How something is created
  • When something is created
  • The number of actions that take place
  • The order in which actions take place
  • The actual type of something
  • The nature of a type (concrete or abstract)
  • An interface
  • The implementation of anything
  • The structure of a collection
  • The current status (mode) of a system
  • The number of modes a system has
  • The cause of a modal change

Learning the patterns teaches you how to encapsulate all of these things, and more. But not everything should be encapsulated. How do we decide what to encapsulate once we know how?

Knowing how to do something empowers you. But then you have to decide under what circumstances to do it and when not to.

This would make for a good sign to put on the wall where you’ll see it repeatedly throughout your workday:

Encapsulate by policy, reveal by need.

Encapsulation is a decision. We know it is inevitable that we will make mistakes, will make wrong choices from time to time. The question is, what will we have to do when we realize we are in error?

If you encapsulate something and then later realize that you should not have encapsulated it (for example, you need to make it reusable or you need to test it in isolation), then breaking encapsulation is usually trivial. You either change it from a private thing to a public thing, or you add some kind of accessor. This is easy.

If, however, you fail to encapsulate something and then realize later that you should have this can be a lot of work, especially if other parts of the system have become coupled to it over time. It can cause a redesign of those other parts. This can cause a lot of rework.

Encapsulating too much can be an easy problem to solve. Encapsulating too little is often very difficult to solve.

I’d rather make the mistake of encapsulating too much.

Practice: Encapsulating Constructors in Simple Classes

A recurring question in OO is, “When it is determined that an algorithm should be established in its own separate class (to make it testable, or reusable, or simply as an issue of cohesion) should it also be given a separate abstract type that it implements?”

The advantage, of course, is that the client (or clients, as more arise) will not couple to the specific service class, but rather to the abstract type. If more versions of the service are added, then clients will be largely unaffected. It’s a form of “future proofing”. But can you do this all the time? Should every class, when deemed necessary, also have a second class that hides its real type? Often this would seem like overkill or over-design. Does that mean it should never be done? That seems wrong too.

Is there something that can be done to resolve this conundrum?

Continue reading “Practice: Encapsulating Constructors in Simple Classes”

The Null Object

Intent

Rather than using a null reference when an object is absent, create an object which implements the expected interface but whose methods have no behavior.

Many of the behavioral design patterns such as State and Strategy allow implementation to vary without specializing clients. Often when a behavior or algorithm has many different versions, one of those versions may be to have no behavior at all. To avoid putting special-case conditional code, “if(!null)” for example, into clients, a Null Object can be used.

Continue reading “The Null Object”