Conceptually speaking, composition models "consists of" relationships, whereas inheritance models "is a".
Using the car analogy, "a car has wheels" is a textbook example for composition, and it makes sense to have a class Wheel
, and a class Car
with a property wheels
of type Wheel[]
.
In theory, an example of inheritance would be "a truck is a vehicle": properties common to all vehicles can be implemented in class Vehicle
, while those specific to trucks can be implemented in class Truck
.
The truck example, however, also illustrates the problem of the inheritance approach: what if you have to make your vehicle class polymorphic not only for the vehicles purpose (passengers vs. freight), but also for fuel type? You'd have to create four classes to cover passenger cars and freight vehicles, as well as diesel vs. gasoline powered. Add another orthogonal property, and the number of classes doubles again. Worse yet, you have to decide which of these orthogonal properties comes first in the class hierarchy: is it Vehicle
-> DieselVehicle
-> DieselFreightVehicle
-> Truck
, or is it Vehicle
-> FreightVehicle
-> DieselFreightVehicle
-> Truck
? Either way, you have to duplicate some functionality, either the freight-specific things, or the diesel-specific things. The solution is to use composition anyway: A vehicle has an engine (which can be diesel- or gasonline-powered), and a cargo type (which can be passengers or freight); you don't need a truck class anymore, because your vehicle class can already model all sorts of vehicles by combining suitable engines and cargo types. Note that the components are still polymorphic, but the container object is not. The trick is to keep polymorphism one-dimensional, that is, each polymorphic hierarchy models one of many orthogonal properties, such as engine type, freight type, etc.
This is why people say "favor composition over inheritance"; of course you are still inheriting, but you have separated your inheritance into independent strains.
The Design Patterns book is descriptive, not prescriptive; it describes the patterns the GoF have observed in the wild; patterns that solve problems.
Some problems can be solved in more than one way.
The alternative you describe is also my preferred approach, but it's also described in the book: it's a combination of the Abstract Factory and Strategy patterns. You use an Abstract Factory as a Strategy.
I don't understand why one would go through the trouble of subclassing a class and making it abstract just for the benefit of allowing it to use a factory in a loosely-coupled manner.
For the exact same reason that you would combine Abstract Factory and Strategy. However, the best solution partly depends on your language.
If, for example, you were programming in Eiffel, Factory Method would probably be easier, because Eiffel has multiple inheritance, but (IIRC) no interfaces.
Also, if you're using a language where you can create a derived value on the spot (such as Java or F#), Factory Method might actually be easier than the Abstract Factory/Strategy combo.
Best Answer
No, the two concepts are different.
A better car analogy would be regarding the engine. Let us assume we have an interface for an engine. It might have properties such as number of cylinders and RPM. You might be able to invoke behavior such as opening and closing the throttle. However, this is just an interface: engine is not an implementation. The Car class has-a engine: when you construct a car, you install something that implements that engine interface. Many cars have different engines as options.
Now there is the implementation. Perhaps we define a
SixCylinderEngine
class. It is-a engine because it implements that interface. It can be used anywhere the engine interface is used.But wait! Some people want a supercharged version of the engine. This is not as simple as changing a variable in the existing engine, this is a major change. Different parts, a second drive belt, another major component (the supercharger), more horsepower, etc.
One way to accomplish this would be to subclass
SixCylinderEngine
and add in the supercharger. That has merit, but can introduce complex interdependencies between the subclass and superclass. That would be another is-a relationship.Instead, we define a
SuperchargedSixCylinderEngine
class that implements Engine directly (is-a). In order to reuse code, it contains aSixCylinderEngine
internally and delegates certain functions (has-a).From here, the next step is to have a
SuperchargedEngine
which accepts any engine: 6, 8, 10 cylinder, anything you want.Here is a code example of the difference.
Inheritence:
Composition: