I have a lot of formal education in Object Oriented Programming: a couple of university courses (one even featuring Eiffel), some books and in general a lot of reading. One thing that comes to short in my opinion is the fact that Object Orientation is all about reducing the state space of a program.
In general, there is a lot of focus in OO about carefully crafting your objects, finding the right abstractions and the right hierarchy for the domain you are trying to model. Given the fact that I had only a handful of cases in my entire career where this could be applied I think that this is the wrong focus. We mostly build layered applications, with pretty simple domain objects that not very often call for very clever design.
What actually should be the focus is the number of different states any given object can have. There are a couple of rules of thumb that I came to believe in while programming. I think a lot of them are actually influenced by Josh Bloch’s excellent Effective Java and one of my all time favorite OO article Object Calisthenics.
1. Use Classes to give names to things
An idea that comes from Object Calisthenics is to create a class for everything, which in general is a good idea. Strings, Lists and other basic library types are not a thing in itself. They are just the very basic building blocks for your program. You rarely just have a String of random characters, you have a Name, an Id, a Title, a Position. You almost always want the additional security of a specific type that signifies what this String of characters actually represents. This also gives you the added value of having a defined state and creating Null Objects in case a value is missing. This also makes for easier readable signatures and return values. Plus you can attach additional functionality to that object and unit test it.
2. Have a way to create the object for all defined states
In a language like Java, where constructors are unnamed, I generally believe in having static constructors. They give you the ability to give a name to what you are actually doing, adding more semantics to the object creation than just “new”.
They also allow you to offload the hard computations in a static method while the constructor in itself only makes sure that everything is in a valid state and immutable. Use Guavas Preconditions or Lombok to make sure you know the nullability of all your fields.
Plus you can have a defined Null Object for your class that comes in handy when you do need it.
3. Have only defined state transitions
I’m a huge believer in immutability and with it comes the need for creating explicit state transitions and copies for all changes. This also makes sure that you can only transition from one valid state to another and don’t have something in your program that is in an unexpected state.
In Java that obviously leads to a lot of verbosity, because you have to write a lot of code that actually validates state and ensures immutability, but it is usually worth it if you try to understand what the code does, at 4am, after a long night out, slightly drunk and with a cold coming up 😉