Skip to main content

Better Software Design: Object Oriented Design

Beyond Procedural Programming
In the old days, programming languages were procedural. That means they accomplished separation of concerns through placing code for each concern in a separate procedure (sometimes called a function or a subroutine). Like many developers, I learned procedural programming before object oriented programming languages were popular. We need to be warned to avoid anti-patterns (common big software mistakes) that come from trying to program in an object oriented programming language, in a procedural way. Let's take a look.

How is Object Oriented Design Different?
Object oriented design accomplishes separation of concerns by placing code for each concern in its own class or interface. Procedures in these classes and interfaces are called methods. Some examples of object oriented programming languages include Java, Objective C, C++, and, sometimes, C#. Also, many traditionally non-object oriented programming language include some object oriented features. For example, these languages include JavaScript, Python, and PHP.
Object oriented design accomplishes separation of concerns by placing code for each concern in its own class or interface.
The Dangers of Anti-patterns in Object Oriented Design
Before becoming a master developer, you may have attempted to program in an object oriented language in a non-object oriented way. Or, you may have designed the code to be partly object oriented and partly not object oriented. Code with such anti-patterns might kind of work for a while, but to be master programmers, we want to take our software development skills to the next level and produce the highest quality code. Code with anti-patterns is very low quality and causes serious software problems.

The Way of the Non-master
Using pseudo-code (plain English that resembles programming languages), we will consider an example. Imagine we received a project to rewrite a program that helps a user create a widget and save the widget for future use. The widget can be created in one of two colors: red or green. As is often the case with legacy software, this program was implemented by a non-master developer (which is sometimes your past self). Implementing that program in an object oriented language in the following way, would have been an anti-pattern:

Version 1:
  • class WidgetSaver
    • method start
      • Create the desktop UI
      • Add options to the desktop UI for a red or green color
      • Add a button to the desktop UI to save the colored widget, and attach the button to the save method
    • method save
      • Create a flat file
      • Retrieve the red or green color of the colored widget from the desktop UI
      • Save the colored widget of that red or green color to the flat file
We are assigned to rewrite the program to use a web UI instead of a desktop UI, to use a relational database instead of a flat file, and to add the option of the third widget color: blue. A non-master attempt may look like this:

Version 2:
  • class WidgetSaver
    • method start
      • Create the web UI
      • Add options to the web UI for a red, green, or blue color
      • Add a button to the web UI to save the colored widget, and attach the button to the save method
    • method save
      • Create a relational database record
      • Retrieve the red, green, or blue color of the colored widget from the web UI
      • Save the colored widget of that red, green, or blue color to the relational database record
As you will see, you did not have enough separation of concerns. At first this rewrite might seem short and simple enough. But, the program was intentionally oversimplified for the purposes of this example. A real program to do these things could be very long, and such a rewrite could be very complicated.

We will see that the ideas (methods) used to separate concerns in Version 1 no longer seem to serve their purpose effectively. You would have needed to make changes in many areas of the code to update code for each concern (the UI, business model, or persistence). Making the same kind of changes in multiple places takes longer and is prone to errors such as forgetting a place where changes are needed, or introducing inconsistencies. For example, it would have been easy to make mistakes like this:
  • class WidgetSaver
    • method start
      • Create the web UI
      • Add options to the bwe UI for a red, green, or blue color
      • Add a button to the desktop UI to save the colored widget, and attach the button to the save method
    • method save
      • Create a relational database record
      • Retrieve the red, green, or ubel color of the colored widget from the web UI
      • Save the colored widget of that red or green color to the flat file
Did you see how the changes were made fine in some parts of the code, but in others there were typos or missed changes, leading to inconsistencies?
  • In some places "web" was mis-typed as "bwe", and blue as "ubel"
  • The program still attempted to add the button to the desktop UI instead of the web UI--even though the desktop UI hadn't been created
  • The color blue was left out of save instructions
  • The program still attempted to save the widget to the flat file instead of the database--even though the flat file hadn't been created
This example might seem ridiculous, but these are exactly the kinds of problems that happen all the time. These problems are worse with bigger programs.

Implementing Object Oriented Design
The way to avoid the anti-pattern above, would be to put some thought into the program design before diving in and writing the program. A good question to ask yourself, is "What are the concerns in this program that should remain separate, and what are the logical classes and interfaces to create to separate those concerns?"

For this example, we might recognize that the widget is the central concern of the program, that the color is a separate concern, and that we will probably want to change the user interface and persistence without changing the business logic and vice versa. Therefore, a logical design may include these high level types:
  • class Widget
  • enumeration Color
  • class WebUserInterface of interface UserInterfaceable
  • class RelationalDatabaseRecord of interface Persistable
  • class WidgetSaver
At first this design might seem more complicated, because the example is so simple. But in complex software, it soon becomes easy to see that such good design has many advantages.
A good question to ask yourself, is "What are the concerns in this program that should remain separate, and what are the logical classes and interfaces to create to separate those concerns?"
Example of Better Design:
  • class Widget - Has property: Color
  • enumeration Color - Has possible values: RED, GREEN, BLUE
  • class WebUserInterface (of interface UserInterfaceable)
    • Use a web UI
    • method build
      • Create
      • Add options for each possible value of Color
      • Add a button to save the widget, and attach the button to the save method of WidgetSaver
    • method retrieveColor: Retrieve the color of the widget
  • class RelationalDatabaseRecord (of interface Persistable)
    • Use a relational database record
    • method saveWidget
      • Create
      • Save the widget of its color
  • class WidgetSaver
    • method start: Build the WebUserInterface
    • method save: Save the widget using the RelationalDatabaseRecord
The resulting code is compact and orderly, and the changes are isolated to the area of code they concern. It is easier to change without introducing inconsistencies. Often, when a customer requests a certain type of software changes, they are likely to request more similar changes in the future. With this design, it would be relatively very quick and easy to add, remove, or change colors, or to change to using a different type of UI or persistence in the future. For example:
  • class Widget - Has property: Color
  • enumeration Color - Has possible values: RED, YELLOW, BLUE
  • class MobileUserInterface (of interface UserInterfaceable)
    • Use a mobile UI
    • method build
      • Create
      • Add options for each possible value of Color
      • Add a button to save the widget, and attach the button to the save method of WidgetSaver
    • method retrieveColor: Retrieve the color of the widget
  • class CloudStorageRecord (of interface Persistable)
    • Use a cloud storage service
    • method saveWidget
      • Create
      • Save the widget of its color
  • class WidgetSaver
    • method start: Build the MobileUserInterface
    • method save: Save the widget using the CloudStorageRecord
For programming in an object oriented language, by investing a little extra time up front to implement high quality object oriented design, you will be well on your way to becoming a master software developer.

Comments

Popular posts from this blog

Reality Checks to Demystify Buzzwords

As an IT insider, I feel I have something valuable to offer nontechnical people in terms of correcting misinformation. Here are a few simple tests for some popular buzzwords in tech. When evaluating a product or service, if you can honestly answer Yes to the reality check question, the buzzword probably truly applies. If the answer is No, it is probably fake. agile Does it make the developers happy? blockchain Does it cut out the middleman? cloud Does it automatically scale? microservice Does it only do one thing? object oriented Is it mostly made of interfaces? RESTful When requests arrive at a certain address, are they ready to use (without parsing)? unit test Does it prevent the tested code from touching anything outside itself?

The Importance of Direction

Which would you say is more important: getting somewhere faster, pushing something harder, or going the right direction? It should be obvious that no matter how much speed or power you use, that won't do any good if you're going the wrong direction. It could also be pointed out that early in a journey, even a small change in direction makes a big difference in where you end up. Therefore, we should make sure we have our direction correct, as the first priority.

How (Not) to Handle Different Exceptions

Came across this sample from a certain multi-billion-dollar company, purporting to show how to implement exception handling. I slightly changed a few cosmetic details to make it anonymous. try { // ... } catch (GeneralException e) { if (e instanceof SpecificExceptionA){ // ... } else if (e instanceof SpecificExceptionB){ // ... } } This is a true actual story--you can't make this stuff up. Yeah, I thought it was pretty hilarious; so I felt like I had to share it.