A computer program is a network of interacting modules. Over time, programs usually become very difficult to change as new features are added and bugs are fixed. More time and effort is put into keeping the system working than is spent adding new features. Build times can increase out of control. These maintenance problems are caused by not carefully managing the dependencies between the modules in the program.
The ideas and techniques used in Object Oriented Design (OOD) can be used to manage the dependencies between software modules, producing software systems that can be maintained and enhanced over their lifetime.
The powerful and pragmatic use of OOD that we teach stresses the creation of modules managed dependencies. This means that the software can be more easily changed. Defects are more likely to be localized, and side affect defects lessened.
Imagine you have a simple program that draws two types of shapes, circles and squares. How would you refresh the screen? Without OO you would iterate over the collection of shapes in the drawing being displayed, interrogate each shape to find out if it is a circle or a square, and then call a specific function to get that particular shape drawn (e.g., you might call drawCircle if you determine that the current object is a circle). Your code would consist of a switch statement, switching on the type of shape and calling the specific draw function accordingly.
It isn't that this approach doesn't work - it does. The problem is that it is difficult to maintain. What happens when the requirements change and we have to draw triangles - after all, we have to draw two types of shapes already, can the third type be far behind? We have to go into the switch statement and work in this new type of shape. Those of you who have worked on large systems know that this switching on a type logic doesn't just happen in one place in your code. The same logic is replicated in many different places. Over time the logic becomes confusing due to special cases that introduce nested if/else logic in various branches of the switching logic. Finding all of these places and inserting a new type at just the right place, while keeping the application working is a BIG DEAL in large, complex systems.
In an Object Oriented Design, deciding what to do based on a type is not necessary. Objects know their own type and their own behavior. The logic for refreshing the screen is a simple loop that iterates over a collection of shapes and invokes the draw function for each shape. When different kinds of shapes are added, the screen refreshing code never has to change because it has no knowledge of specific shapes. This is the hallmark of a good design - the ability to extend and enhance a system by adding more code, instead of the old way of working in new features and trying not to break anything in the process.
In order to produce a good Object Oriented Design, one has to know how to discover and design the objects that are needed for a given application, how to recognize what a good object is, and how to connect the objects together in a way that doesn't yield a rat's nest of inter-dependencies.
At Object Mentor, we follow a set of guidelines that we call The Principles of Object Oriented Design. The shape drawing example given above employed one of these principles, namely the Open/Closed Principle. If you follow these principles as you are designing your software, you are more likely to produce a high quality design — a design that allows for future extensions and enhancements. The Principles are available as publications here on our website, we cover them in all of our programming language classes, and they are studied in depth in our design classes.