Practically every modern programmer has heard about object-oriented programming. It’s been around in one form or another since the early 1960s but gained popularity in the mid-1990s and has been going full steam ever since.
For those new to the term, it’s nothing more than a way of thinking about how to design a computer program. Rather than thinking of the steps that you take to solve a problem, you picture the parts of the program as being things that interact with each other — essentially a model of some reality that exists outside the computer program, mimicking the real-world problem you’re trying to solve.
Objects? Models? I’m not sure I get it…
This isn’t the simplest concept to wrap your head around but it’s definitely worth learning. Let’s start with “what’s a model?”.
Dictionary.com defines the word model as “… a representation, generally in miniature, to show the construction or appearance of something” (Model). For our purposes that’s a great definition, because when we’re modeling the computer system, we’re creating a simplified version of the real thing that we’re interested in.
Let’s take a person for example. If we’re describing a person to a computer system, we might think of attributes like their first name, last name, and birthdate. And, depending on what our system is doing, we may include things like social security number (if we were the IRS) or shoe size (if we were an online shoe store). There’s no way that we’ll get the absolute totality of the person described to our system, but we will include the things we consider important to solve our problem (a thing that software architects call the problem domain).
So, our model of a person is simplified. It takes into account only those things that we find important at the time and, as we know, businesses change what they consider to be important, so this model will evolve over time. That’s okay. We won’t get it right from the start, and we should expect that.
Our person model isn’t going to live in a vacuum. There will be other kinds of objects that they will interact with. A person might have an address, or more than one address and if that’s important to us we could bottle that as well.
Let’s give an address a house number, a street, a city, a state (or province, or something similar) and a postal code. And our person can have 0 or more home addresses since we need to handle both a homeless individual and someone who winters in warm climates. They may have zero or more work addresses. If that’s important to our application, we’ll put it in our model.
Our model currently looks like this:
Using this approach, we’ll grow the model as we talk to the business folks about what their problem domain looks like. Every important noun they use in describing what they want the system to do becomes a part of our model, so take good notes! This brings us to the first principle of object modeling:
Objects are nouns
The things we’re modeling are just that … things. They’re nouns in the conversation we have about what our system does. If you find yourself modeling “Run” or “Sing,” you’re doing it wrong.
At this point we need to stop and correct some terminology. We’ve been saying “objects” because that’s what everyone says when talking about this kind of modeling, and that is the correct term — object modeling. But the things we’re designing are properly called classes. A class is the type of the object, and the object is the specific instance of a class.
An example will make this clearer. Earlier, we designed a Person class and an Address class. In our system, we might have Joe, Sally and Jim Bob — they’re objects of the Person class. If Joe and Sally both live at 123 Main Street, and Jim Bob is their neighbor at 125 Main Street, we have three Person objects and two Address objects (Joe and Sally sharing the Address object for their Home Address property). From here on we’ll use the terms class to describe the kind of thing and object for the instance of the thing.
So, Classes are nouns
Better. Moving on…
Classes can do things
This is something that’s often lost when programmers don’t completely think through the fact that they’re doing object modeling. In today’s world, there are many ways of storing objects in some persistent way, like in a database. Programmers can use code libraries like Microsoft’s Entity Framework, Red Hat’s Hibernate (and its open-sourced cousin NHibernate), Apple’s Core Data, and hundreds of others, both commercial and free. They’re great time savers but have a tendency to make the programmer think of classes only in terms of their attributes. And that’s truly a shame.
In every programming language that supports classes and objects, their definition includes both their attributes — characteristics of the objects; the data they contain — and methods, computer code that allows the objects to perform actions. They’re not just anemic data bags and shouldn’t be allowed to be modeled that way.
Another example would be good here. Let’s add the attribute Spouse to our Person class. Spouse will be an object of type Person and start as empty (or null in computer talk) since a Person isn’t born married.
So, back in the day Joe and Sally were single, but one day Sally popped the question. Joe said yes, so we run the Marry() method of the Joe object. Our Marry() method needs to know who Joe is marrying, so we’ll pass the Sally object to it, as Joe.Marry(Sally).
Now, our model looks like this:
Inside that method we can set Joe’s Spouse attribute to Sally and tell Sally’s Spouse attribute that it should contain Joe. We can, of course, do other things, like reduce their SavingsAccount’s Balance attribute because they had a big wedding, but that would complicate things. The point is that the method call in the Person class does the things that make sense to change both the current object and any other associated objects following whatever business rules we had set up there. And those rules will always be followed for any object of the Person class because they’re part of the class, making them easy to find and change when the time comes. And, yes, business rules do change, but you already knew that, didn’t you?
The model should always reflect reality
Since we’re focusing our class’s attributes on the things we care about for our system, and adding methods for rules that each object must follow when they do their appropriate actions, we need to make sure the model stays up to date with changing realities in our business.
If we’re modeling a grocery store, we’ll have classes for Merchandise, Sale, Customer (if we have a loyalty program), and others. Merchandise might have an attribute called Taxable where, if it’s true, we get sales tax for selling it, and bypass the sales tax if it’s false. Let’s put a Sell (Customer, Merchandise) method in the Sale class to do the work of selling something to a particular Customer.
Our business rules say that, if the Merchandise object’s Taxable property is true, we look up the tax and add it to the total. Great — calculating and collecting sales tax just became automatic, since our Sale object does it when we sell something. But then the rules change…
Once a year, right before back-to-school time, there’s a weekend where school supplies don’t get a sales tax charge. What to do?
Rather than get into any of the hundreds of ways we could handle this, this illustrates that we need to ensure that our classes stay up-to-date on the changing realities of our problem domain. Not only do we need, in this case, to suspend sales tax on certain items, but that suspension has to be temporary. And the dates will change every year, so simply saying that, for the next two days, some items won’t be charged a governmental surcharge won’t fly. Coming up with generic ways of handling this kind of problem is what programmers live for.
The TLDR of it all
(Which stands for “too long — did not read” for those not fluent in geekspeak.)
Boiling it all down:
- Create a model — a simplified version of reality — that contains the types of nouns you’re interested in for your application.
- Add attributes that are important to these interesting classes
- Any actions an object performs should be performed by that object
- Make sure that, when reality changes, the classes change too.
From here it can get more complicated, but this is a great start. The trick is to remember these basic principles when you start getting lost in the complexities that come later. Forget them, and the application starts getting brittle and buggy. But if you stick to them, changes are easier, bugs are fewer (they’re never completely gone), and the investment in creating the application can continue to pay benefits for many years to come.