Classes allow for inheriting from super classes, overloading methods, etc. while one could also create delegate objects and then access their methods.
Outside of getting overloading and signature matching, are there any good rules for deciding when to use inheritance vs delegation?
The underlying test here is the coherence of the resulting decomposition. If you look at the problem space and it seems pretty clear that there is a set of things which all belong to one type, but there are subtypes within that type, then you have a classic setup for generalization, aka inheritance. Whereas, if you have a complex set of behaviors in which there seems to be a subcategory of behavior and knowledge which can be treated as a cohesive unit separate from the rest of the behaviors and knowledge, then you have a good candidate for a delegate. Thus, in simpler cases, the two don't really compete as much as they relate to different ways of breaking up the problem space.
The place where they become more closely related is in complex cases where one has what amounts to "cross-tab" subtypes. E.g., orders might be subtyped according to the customer type, internal versus external, since there is different knowledge and behavior associated with those types. But, orders might also be subtyped by delivery method - shipped, dropshipped, pick up, virtual or licensed, etc. And, each of the delivery methods might apply to each of the customer types or nearly so. Unless one of these classifications is clearly primary to the other, this creates a messy situation for generalization because one has to duplicate subtype breakdowns. This is very fragile if there are changes and the duplication is a very bad sign. Some such situations can be handled with multiple inheritance, but fortunately we haven't been given that particular way to shoot ourselves in the foot in ABL. Instead, one looks at this and wonders if there is an opportunity here for a delegate. My instinct in this particular case is to turn the shipping method into a delegate which has its various subtypes and to leave the customer type as subtypes of Order itself. This solves the crosstab problem since we now have two separate simple generalization hierarchies.
There is some discussion of these ideas in
http://www.cintegrity.com/content/%E2%80%9CGang-Four%E2%80%9D-Decorator-Pattern-What-Its-Appropriate-Use
although the focus there is on a proposed third approach.
I try to look at the relationship between the two objects being modeled.
'Is a' Relationship - Use Inheritance
For example, a circle is a shape.
So you would expect the Circle class to inherit from the Shape class:
CLASS Circle INHERITS Shape:
...
'Has a' Relationship - Use Delegation
For example, a car has a drive shaft.
But a car has other parts as well - a car is not just a drive shaft, so you wouldn't use inheritance.
You would use composition (delegation).
CLASS Car:
DEFINE PRIVATE VARIABLE driveShaft AS DriveShaft NO-UNDO.
...
Obviously real world examples are not always this easy or clear. But the rule should still apply.
I would not equate delegation and composition.
From http://www.cintegrity.com/content/Object-Oriented-Vocabulary-Introduction
Delegation: One object relying on another to implement a part of its overall functionality. A Delegate is created as a separate object to further separation of concerns, particularly when the main object is very complex and the Delegate has variations appropriate for Specialization. Once separated, the Delegate and the original Object are peers, each with their own separate sphere of Responsibility. It is important that this separation exist in the problem space, i.e., the Delegate should be an intrinsic decomposition recognizable in the problem space, not merely an arbitrary cluster of knowledge and behaviors. The container object, i.e., the one that has Delegated some of its behavior, may expose Methods by which that Delegated behavior can be accessed so that Clients of that object need not be aware of the Delegate.
Composition: A relationship between one Object and one or more other Objects in which one object “contains” the other objects or which one would describe by the phase “part of”, i.e., some departments are “part of” a university. Composition differs from Aggregation in that in Composition, destroying the container object also destroys the contained Objects, if a university is disbanded, the departments no longer exist.
and, since Composition is contrasted to Aggregation
Aggregation: A relationship between one Object and one or more other Objects in which one object “contains” the other objects or which one would describe by the phrase “part of”, i.e., some group of teachers is “part of” a school. Aggregation differs from Composition in that in Aggregation, the contained Object has the potential to stand alone, i.e., if the school is disbanded, the teachers still exist.