NOTE: The articles I’m writing about code quality will be prefixed with [Precise Programming], and tweets will include #PreciseProgramming.
In object-oriented programming, our classes usually represent real-world things. And we’re often told “everything is an object”.
However, I frequently see projects with classes that sequentially call function after function – many of those functions being hundreds of lines long. This is procedural code, being forced into an object-oriented way of programming.
At first, I usually thought the problem was that the original author didn’t understand object-oriented programming principles. That’s true in some cases. But, there are many situations with real-world “things” that do not map to “objects”.
When I think about a project I’m working on now, the functionality fits into a few different real-world equivalents. This seems to be true for most other custom business applications I’ve worked on over the years.
Here are the different types of components I see in most programs – using an order entry program for an example.
Mutable business objects: These are what we normally think of as “objects”. They represent some “real-world” entity that our program is trying to emulate.
These objects are mutable – they can change, while running the program. In fact, the main purpose of the program is for these objects to change.
The main mutable object for our example project would be an Order.
The customer creates a new Order, adds items to it, adds a shipping address, and pays for it with their credit card (changing the Order’s status). Then, the Order will be packed, items may be back-ordered, and the order will be shipped and delivered.
C# handles mutable objects well, although we will want to move some of the code out of the business objects, and into the other components below.
Immutable business objects: This covers information the program needs to read, but will never change – “reference data”.
For example, the tax rate on the items we sell. Some items may have different tax rates (0% for medicine, 2% for food, 10% for miscellaneous, etc.). And, it is possible that these tax rates may change.
However, it would be more accurate to say that the rate would be “replaced”. If the government changes the sales tax rate, every order (from that time forward) will use the new rate.
Our program should not treat these the same as objects that are designed to be changed while the program is running.
In C#, the closest things we have to represent this type of data are enum and struct. However, there are some problems with using them. Enums don’t have additional properties, which may be useful. Structs don’t allow inheritance, which may also be useful.
It will require some experimenting, to find a good way to handle this type of functionality.
Workflows/State Managers: Our program is automating a manual business process – a workflow. Instead of having people write orders on papers, and giving the paper orders to people in the warehouse, the computer is doing that for us.
In most programs, there are many possible workflows. Orders with food need to be delivered overnight, with special packing. Orders shipped outside the country need a Customs declaration, and maybe can only be delivered by air freight.
This is where I often see object-oriented code get very ugly. Classes become very tightly coupled, and the programmer needs to remember to execute certain functions in a certain order – or the program crashes. Oftentimes, the workflow rules are implemented across several different classes, instead of being in one place (which makes them easier to maintain).
I’ve used different queue libraries, which could handle the workflow requirements. However, many of them are complex to work with. I haven’t seen a good C# state machine library, although I haven’t really looked yet.
As much as it would be fun to build my own workflow engine and state machine library, I’m going to try to find existing tools first.
Service Providers: These are external resources the program needs, in order to be complete, but really aren’t part of the business purpose of the program.
Once a company packs its orders, they probably won’t have their employees deliver the packages. They’ll hire UPS or FedEx to do that. The business doesn’t want to manage deliveries, so it uses an external service provider.
For our program, the database could be considered an external service provider.
We need to save our orders to a database, read them, update them, create reports from them, etc. However, we don’t want to write our own version of SQL Server. In fact, we may not even want to write the code that converts between our objects and our database. We may “outsource” that work to an ORM (Object-Relational Mapping library), such as Entity Framework or NHibernate.
For our program’s service providers, there is an extra requirement we have. We want something we can easily “mock” – replace with our own fake version, to allow us to reliably test our program.
My current plan to manage service providers is to have a static ServiceManager class that instantiates the required service providers, while having a way for us to replace them with mock service providers when testing.
This seems to be a much more accurate model of what a program does – instead of trying to force these different types of functionality into OOP.
I’m including these ideas in a test program I’m writing. I’ll post the code for it soon, when it looks like the major pieces are in place.
If you have any thoughts on how to do any of these different parts of a program, or any suggestions of libraries to use, please leave a comment below.