Relationships: is-a and has-a – Object-Oriented Programming

Relationships: is-a and has-a – Object-Oriented Programming

Relationships: is-a and has-a

The inheritance relationship between a subclass and its superclass is embodied by the is-a relationship. Since a subclass inherits from its superclass, a subclass object is-a superclass object, and can be used wherever an object of the superclass can be used. It has particular consequences for how objects can be used. An object of the TubeLight class is-an object of the superclass Light. Referring to Figure 5.1, an object of the TubeLight class can be used wherever an object of the superclass Light can be used.

The inheritance relationship is transitive: If class B extends class A and class C extends class B, then class C will also inherit from class A via class B. In Figure 5.1, an object of the SpotLightBulb class is-an object of the class Light. The is-a relationship does not hold between peer classes: An object of the LightBulb class is not an object of the class TubeLight, and vice versa.

Whereas inheritance defines the relationship is-a between a superclass and its subclasses, aggregation defines the relationship has-a (also called the whole–part relationship) between an instance of a class and its constituents (also called parts). Aggregation comprises the usage of objects.

A class declaration with instance field references implements an aggregate object. The class Light is declared with three instance fields: an instance field to store its wattage (noOfWatts), an instance field to store whether it is on or off (indicator), and an instance field reference (location) to a String object to store its location (in fact, its only constituent object). An instance of class Light has (or uses) an object of class String. In Java, a composite object cannot contain other objects. It can only store reference values of its constituent objects in its fields. This relationship defines an aggregation hierarchy (also called object hierarchy) that embodies the has-a relationship. Constituent objects can be shared between objects. If their lifetimes are dependent on the lifetime of the aggregate object, then this relationship is called composition, and implies strong ownership of the parts by the composite object.

Choosing between inheritance and aggregation to model relationships can be a crucial design decision. A good design strategy advocates that inheritance should be used only if the relationship is-a is unequivocally maintained throughout the lifetime of the objects involved; otherwise, aggregation is the best choice. A role is often confused with an is-a relationship. For example, given the class Employee, it would not be a good idea to model the roles that an employee can play (such as manager or cashier) by inheritance, if these roles change intermittently. Changing roles would involve a new object to represent the new role every time this happens.

Code reuse is also best achieved by aggregation when there is no is-a relationship. Enforcing an artificial is-a relationship that is not naturally present is usually not a good idea. Methods that contradict the abstraction represented by the subclass can be invoked. Using aggregation in such a case results in a better solution.

Both inheritance and aggregation promote encapsulation of implementation. Changes in the implementation of constituent objects generally have minimal impact on the clients of the composite object, since these clients do not directly deal with the underlying objects. However, changing the contract of a superclass can have consequences for the subclasses (called the ripple effect) as well as for clients that are dependent on a particular behavior of the subclasses. For this reason, aggregation provides stronger encapsulation than inheritance.