While the first OOP language was Simula in 1962, my introduction to these techniques was mainly C++, which was first released in 1980. C++ popularised Object-Oriented Programming, and greatly influenced Java which came along fifteen years later, in time to be an incredible popular language for the internet age.
What should I do with prototypes?
In theory, prototype-based OO is quite different to class-based, and there are plenty of academic articles discussing the various pros and cons. I’d like to present the slightly controversial point of view that you shouldn’t pay too much attention to prototypes except for the overall understanding that it’s how classes are implemented.
Missing OO features
Private methods are important only insofar as they are functions that you want to add to a class because they operator on the object’s data members, and yet you don’t want to confuse people who might assume that they are part of the object’s public API than they can depend on.
So, what does “good” OO look like?
The SOLID acronym is a mnemonic to help us remember some important design principles for our OO software.
Single responsibility principle
Each of our JS classes should have do one thing well. Keys to ensuring that this happens include naming our classes well, and not letting them get too long.
Liskov substitution principle
Interface segregation principle
Many client-specific interfaces are better than one general-purpose interface. This will actually come quite naturally to JS developers. Whether you are declaring type interfaces with something such as Flow, or just relying on implicit ones, you don’t need to rely on the entire public API of a class, just that bit that matters for this application.
Dependency inversion principle
Depend on abstractions, not concrete implementations. In a dynamic language like JS, we basically get this for free.
Cohesion and Coupling
If the above points seem a little complex or rooted in statically typed languages, maybe it’s better to instead focus on the general point that our objects should demonstrate high cohesion and low coupling.
Cohesion is the extent to which methods of an object are related to each other. Having high cohesion is much the same thing as obeying the single responsibility principle. One way to check cohesion is to look at which parts of the object’s data each method touches. If a certain group of methods use several data members, and another set of methods use another set of data methods that don’t overlap much or at all, then you have low cohesion and should potentially have two classes instead of one.
Coupling is the extent to which two different objects are dependent on each other. The “L”, “I” and “D” in SOLID are all strategies for keeping coupling low.
When do we use all this?
Good software design is mainly about maintainability. Well designed software is easier to read, reason about, and modify, which makes it easier to return to the code later to fix bugs and add new features.
Code that is easier to modify often has more abstractions, but until we know that we need to modify that part of our code, building highly generalised code can be less readable. Thus, our code often starts quite concrete, and becomes more abstract or general in the places that appear to change often, or we learn are likely to change often.
The process of building good software thus relies on our ability to refactor code so that it starts of fairly specific to today’s need, and becomes more abstract in the parts that change. We do that using refactoring techniques, and we look for opportunities to refactor based on noticing bad smells in the code.
So, if we’re reviewing object oriented code, as well as looking for examples of good design, we need to keep an eye out for smells that haven’t yet been spotted and refactored.
If someone has worked on all the steps for writing good procedural code that I discussed in Part 1, then they will probably begin to notice some smells that can be solved using Object Oriented techniques.
There’s a bad smell here, and we can spot it in a number of different ways. It could be because the high level of cohesion between x and y indicates that they should be in an object together, but the easiest way is simply the duplicate. That’s right, the expression posX + (directionX * delta) is more duplicate code than we should tolerate, we need a 2D vector object here.
Bringing it all together
Use OO to make your procedural code easier to read by building up from basic types to give them more functionality. Use encapsulation more often than inheritance. When dealing with detailed objects of basic types, such as those serialised to JSON, consider applying decorators before you use them in order to give them some needed behaviours.
Up next, is Functional Programming the new black?
Yep, I’m busy with food again. I’ll see you back here for part 3.