Design – Logical vs Physical Entities in Object-Oriented Class Design

designobject-oriented

Going through CS OOP classes a couple years ago, the examples they always used for OO class examples were the standard car/tire/engine and shape examples.

I sat down with the book 'Agile Principles, Patterns, and Practices in C#' and the Coffee Maker example attempted to change my view of OO class design. The book said that classes should NOT be physical objects like a car or tire, but should represent some behavior.

The (bad) example he gave was an intermediate programmer's view of how to design classes related to a Coffee Maker (and exactly how I would have designed it). He eventually went on to create fewer classes such as CoffeeMakerUI, HotWaterSource, and ContainmentVessel (none of which represent any physical part of an actual coffee maker).

Now I'm stuck wondering if I have been thinking about OO design incorrectly all these years and how I can think more abstractly when I design my classes. Should behavior drive how I design my classes?

Best Answer

There is no consensus on what exactly distinguishes object-oriented programming.

Historically, OOP was first “discovered” in the context of simulations, where objects literally represented physical objects and methods on these objects represented external events that the objects can react to. The general consensus is that this is not a good way to write software, but that the underlying principles can be very valuable.

One problem with modelling physical objects is that this misleads the design to include irrelevant relationships between objects, and irrelevant properties of the objects. Also, it is often more helpful to model abstract concepts instead of physical objects.

A more modern view does not see OOP as a technique to translate models into code, but sees object-orientation as a way to solve various problems through polymorphism, primarily interface inheritance.

This is usually a bottom up approach: I have different things that behave differently, but I want to use them as if they were the same type of thing. Without OOP, I have to look at each thing to find out what it is, then execute the correct behaviour.

Instead, I can define an interface that describes how I want to use them. Then I make my existing things conform to this interface, either if they inherit the interface or if I create an adapter between the interface and a specific thing. Now I can tell the things what to do, and they can perform their individual behaviour without me knowing their real type, because they conform to the interface.

It turns out that OOP is an excellent way to achieve modularity and extensibility. By depending only on interfaces instead of on concrete types, I can swap out implementations without further changes to the system (often through a “dependency injection” mechanism). One consequence is that object-oriented systems are easier to test in isolation than procedural systems, because I can easily replace the real dependencies with stubs or mock objects.

Not every software system needs this kind of flexibility. Many problems are a very good fit for non-polymorphic models. Quite often, a class is not used because we want to do OOP, but because that's the only mechanism in the language to implement record types. Most method calls instance.Method() are not polymorphic, but just a convenient abbreviation for what is effectively a static function Class.Method(instance).

In addition to “OOP models the real world” and “OOP uses polymorphic techniques” there are also a number of other OOP interpretations:

  • OOP is defined by Inheritance and Encapsulation (often taught in introductions to programming, but misses the point)
  • Objects = Data + Behaviour
  • Objects use open recursion (B. Pierce)
  • Object-oriented systems are SOLID (Robert C. Martin)
  • Objects communicate through message-passing (Alan Kay)
Related Topic