My current approach to software design is to not do Object-Oriented Design at all. Instead, I come up with a Well-Organized Eclectic Design and implement it using whatever techniques are appropriate. Object Oriented Design techniques can be helpful with this, but trying to use Object Oriented techniques to solve every problem is less than ideal. The anti-Object-Oriented Controller Pattern is an example of a technique that can be used to create a Well-Organized Eclectic Design.
The rest of this is pretty outdated. But the Controller Pattern is always a useful design tool to have handy.
Most intro textbooks on object-oriented design present it as data-centric design with behavior and data combined. The focus is usually on problem domain objects/classes like Vehicle, Car, Boat, Airplane, Train. These classes have functions to provide behavior: Go, Stop, Turn. And data to describe state: Position, Speed, AmountOfFuel.
Where do you put complex behavior that involves multiple objects? Do you spread it amongst the objects? Pick a single object (possibly at random) and shove it in there? Spell check is an example. Does spell check belong in Dictionary, or does it belong in Document? With apologies to all the optometrists out there, which is better?
Dictionary dictionary;
Document document;
// This?
dictionary.SpellCheck(document);
// Or this?
document.SpellCheck(dictionary);
The Controller Pattern is completely counter to O-O. It says to pull the behavior out of the problem domain objects and put it in a central controller "class". It's not a class at all. It's completely functional. Maybe it has some state, but it represents a behavior-centric decomposition. (It's a "module".) So, for spell check, we have a SpellChecker class.
Dictionary dictionary;
Document document;
SpellChecker spellChecker;
// This!!!
spellChecker.Load(dictionary);
spellChecker.Check(document);
What gives? Is O-O wrong? Is "Object Oriented Considered Harmful"? Does O-O only work in textbooks? In a way, yes. Data-centric design with behavior and data combined does not scale. For small problems, like linked lists, combining data and behavior works perfectly. For large problems, like 500,000 line applications, combining data and behavior tends to cause more problems than it solves.
As a system becomes huge, controller objects become more and more necessary to organize massive amounts of behavior (and comparatively little data). Taken to an extreme, the problem domain classes end up being nothing more than collections of data fields. All the behavior is pulled out into controller objects. (Note that at this point trivial getters and setters are ridiculous. Many authors endorse them. I do not. Since the behavior has been pulled out, problem domain objects should just be collections of public member variables.)
So, start with textbook O-O, but don't be afraid to move towards the Controller Pattern even though it feels very wrong. It's not. It leads to a more sensible decomposition and an easier to understand system. And that's the whole point of classes. [No. Start with a Well-Organized Design, then bring O-O and the Controller Pattern to bear where they are needed.]
Jacobson (1992) describes the three most common kinds of classes that appear in a large-scale object oriented system. Entity classes are the problem domain classes. The cars and boats. Boundary classes are the interface classes. The Ports, Communications Protocols, Loaders/Savers, File Formats, and Views. Control classes are the controllers. The behavioral objects. The SpellCheckers, the commands (Command Pattern), business rules, algorithms, etc.... If you keep these general categories in mind, you'll design better systems.
One need look no further than the STL to find examples where behavior has been pulled out of objects for good reason. The standard algorithms could have been implemented as member functions, but they weren't. This was to allow them to work with many different classes.
Craig Larman, Applying UML and Patterns
<- Back to my software page.
Copyright ©2012-2016, Ted Felix. Disclaimer