In Java, as you know everything revolves around “Classes and Object” only. So, you must know about the relationships between classes and objects to model or design any software Application. There are various relationship types exist in java called Association, Aggregation and composition. So, I am going to explain here about the Object-Oriented Programming concepts of Association, Aggregation and Composition and Inheritance.
The most exciting feature of object-oriented programming that mimics real-world objects. In other words, it (our code) tries to replicate real-world objects. When we talk about real-world objects, then the most complicated thing in real-world objects is relationships.
When we talk about the association in java, then this is nothing but a structural relationship, in object-oriented modeling, that specifies how objects are related to one another. This structural relationship can be shown in two forms:
- Class diagram associations
- Use case diagram associations.
Association represents the unidirectional or bidirectional relationship between two classes. If the Customer places an order, then this is a unidirectional association.
Bidirectional Association example: Person and Dog classes that setup a bidirectional association between a Person object and a Dog object. A Person object behaves like an owner for a Dog object and the Dog object behaves like a pet for the Person object. An association may exist between objects of different types or between the objects of the same type means of the same class. An association in UML is drawn as a solid line connecting the object of different classes or two objects of the same class.
If we want to show navigability, then we can use arrow (-->) at one end or both ends for bi-directional navigability. Here, if you see the below diagram then we have navigation from class A to class B. So class A somehow related to class B. So, we can say that class B is navigable from class A.
There are many types of associations in Java-like one to one, one to many, many to many and many to one.
In Aggregation, the based object is standalone and can exist even if the object of the control class is dead. Let’s take an example: suppose we have a class Car and car has a dependent object as a Wheel. Now when we destroy the car, the Wheel object is still alive because the wheel can be fit into different cars. So, here the association between Car and Wheel is the aggregation.
First of all, to exist an aggregation relationship, there would be some association. In a plain association, classes are considered at the same level with one class having no more important than the other. Sometimes we do need to define a “whole/part” relationship where one class considered as a “whole” that consists of smaller classes that is considered as a “part”. This kind of relationship is called aggregation. It is also known as “Has-A” relationship as object of the whole has the object of the parts.
An aggregation in UML is drawn as an open diamond as below:
Multiplicity here is: 1 means Exactly one instance and * means zero or more instance
See the above diagram. Employee is related to a company. Java Development Company can have one or many employees.
You can see the open diamond symbol. So, whenever you see open diamond symbol, that means it represents the aggregation in UML.
Aggregation and composition are both powerful forms of association. They both describe relationships between a whole and its parts. So, instead of “Has-A” relationship as a simple association, we deal with relationship that says is “part of” or reading relationship in other direction is “made of”.
Examples of this kind of relationship is Band made up of Musician or in other words Musician is a part of Band. Another example is Catalog made up of Product or we can say that Product is part of a catalog.
1.*represents here At least one instance
As we have seen aggregation relationship makes a distinction between “whole” and “part”, but it does not force ownership. Also, the aggregation does not link the “whole” and “part” object in such a way that if the whole is destroyed then parts are also destroyed.
There is a variation of aggregation called “composition”. This has strong ownership, thus the scope of the whole and part are related. In composition, if there are two classes, class A(considered as a whole) and class B(considered as part) , then the destruction of class object will mean that class B object also does not exist. This also means that class B object may be a part of class A object only. So, this is a tight association. Composition in UML is drawn as a “filled diamond” at the whole end.
So, here in the above diagram, we have a Department class and school class. There is a relationship exists from department to school.
The composition is an even stronger relationship than aggregation. To test for whether you are dealing with composition, you should use the “no-sharing rule”. This rule states that in a composition relationship, the part can belong to only one hole. So, in our example above does musician belong to only one band? This is not true. A musician can belong to a number of different bands. Same with the product-catalog relationship. Any particular product could appear in a number of different catalogs.
Now consider a relationship between a building and room as below. Can a room belong to more than one building? It can never be. So, this relationship is better described by composition than by aggregation.
So, a composition relationship states that in this relationship the part can belong to only one whole and no sharing. Related to that if the whole is destroyed, then the part that makes it up is also destroyed. So, in our example, if we destroy a building, then the rooms also will get destroyed. On the other hand, with aggregation, this is not necessarily true. In our previous aggregation example of Band and Musician, suppose if a brand breaks up then the Musician that is part of this will not get destroyed and he/she can perform for other bands as well.
So, the main points about Composition in java that it supports has-A relationship and it can be implemented using instance variable in java. Composition supports code reusability. For example, if you consider the car class then the car has an engine. So, we can write like:
So, here we have implemented has-A relationship between Car and Engine using an instance variable. We can re-use the Engine class in other Car classes also thereby entertaining the code re-usability. Composition hides visibility to the client classes. Here, we can implement the Car in such a way that Engine class is not visible to client class. This is our choice of implementation.
Let’s see how we can achieve this:
Note: Here I have not provided a getter/setter method for Engine object. Because I want to hide the implementation from the outer world.
So, in my constructor, I am passing engine type, name and power and creating the engine object here. Then I have created the getter methods to retrieve the engine type, name, and power.
Aggregation vs composition Summary
- Aggregation and Composition are a special type of association and differ only in the weight of the relationship.
- Composition is a powerful form of “is part of” relationship collated to aggregation “Has-A”.
- In Composition, the member object cannot exist outside the enclosing class while same is not true for Aggregation.
Inheritance is basically Is-A relationship. We can achieve inheritance with the help of extends keyword. I believe you are already aware of the inheritance basics and the types of inheritance. I am discussing here the advantages of inheritance and how it is different from the Composition.
Main advantage of inheritance is code reusability. Suppose if Class B extends Class A, then all the properties of class A come into class B and we can access all the properties of Class A in Class B expect the private members. Also, we can call methods of A class using B class object.
Through inheritance, we can reduce redundancy.
Suppose we have a Class Car and Class Bus. Both of them are having startEngine() and stopEngine() method. Car class has openSunroof() and closeSunroof() method. Bus class have openGate() and closeGate() method.
So, here both Car and Bus class have same method startEngine() and stopEngine(). So, we have duplicate methods here and this is really a complex thing to maintain these duplicate methods in multiple classes.
So, for solving this problem, we can use inheritance. We can create a Vehicle class with two methods startEngine() and stopEngine(). Then we have to create two subclasses Car and Bus and these two classes will implement the Vehicle class to use the startEngine() and stopEngine() methods.
So, Inheritance entertains tight-coupling between the classes which is a disadvantage. Here Car class and Bus class is tightly coupled to Vehicle class. Car is a type of vehicle and same for bus also. So here Car is a vehicle. So, all the properties of a vehicle will get inherited to Car and Bus.
Deep dive: Composition vs Inheritance
Problems with inheritance:
Already I discussed that with inheritance, we can get code re-usability. But with inheritance, it is easily abused by amateur developers which can lead to larger hierarchies of the classes. But such hierarchies are fragile. If you change the class on top of the hierarchies, then all the classes inherited from that class will be affected and this is because of the tight coupling that comes with inheritance.
Imagine we are designing a game and we would like to introduce the concept of a Person and a Dog. Both Person and Dog class can have attributes such as Age and methods such as eat and sleep (). So, with inheritance, we can create an Animal class and then derive Person and Dog from the Animal class like below.
Now, let’ say, we need to give Person and Dog the capability to walk. So, we need to go to our Animal class and change that class by adding a new method called walk(). The problem at this stage is both Person and Dog derived from Animal class will get affected as a result of the addition of walk() method to their parent class Animal. Sometimes, when you change a class, the dependent classes may have to be changed or at least it has to be re-complied and re-deployed.
So, now to make the matter worst, let’s imagine a situation where we have to introduce another concept called Fish to our existing game. So, here we have to add a new class called Fish that is derived from Animal class. But there is a problem here.
The problem now will arise here. The problem is that Fish cannot walk. So, our inheritance hierarchy needs to be modified. That means we have to take the walk() method out of the Animal class.
So, we can create another subclass called Mammal where it will have the capability to walk and then we can derive Person and Dog class from Mammal. So, here the hierarchy is getting changed again. This the problem with Inheritance.
Any inheritance relationship can be translated to Composition. This can result in great flexibility. Now, I will show how we can model the same classes (used in Inheritance example) using Composition.
As shown in the above diagram, instead of Person and Dog derived from Animal, they have an Animal. In real-word, a person is an Animal that does not mean we should always use Inheritance or Is-A relationship. We as software developer need to think differently. We need to design our classes such that any change in class should be isolated and should have minimal impact on the other classes of the system.
Let’s say we forgot to give the Person and Dog the ability to walk. Now we can model that using composition. We have to introduce a new class called “Walkable” which has a walk () method. This Walkable class describes the behavior of walking for any kind of Animal. Now we have to create a composition between Person and Walkable class and the same for the Dog class as well like below diagram.
Now what happens here is that A person has an Animal and also it has a Walkable. So, Person and Dog class has the capabilities that are defined in both Animal and Walkable class. Now suppose we want to add another class called Fish. So, for that, we have to create a class called Fish that is composed of animals. Fish cannot walk so I am not going to create an association between Fish class and Walkable class here.
Note: In Inheritance, simply adding a Fish class broke the hierarchy. But here we simply added new Fish class and created composition between Fish and Animal. Here, Person class is not modified and also Dog class is not got modified. So, using composition we can create any number of new classes with new capabilities of the existing classes. We compose them together and this has minimal or zero impact on the other classes.
So, with composition, we got great flexibility and also, we got loose coupling. Let’s take a look at both the composition and inheritance relationship in terms of coupling.
So, in the above diagram, I have given an example of both Inheritance and Composition. In this example Coupling between Person and Animal is equal. Whether to use inheritance or composition?
If Animal is changed and Person may have to be changed or at least needs to be re-complied or re-deployed. With Composition, we get an extra benefit that we can not get with Inheritance. We can replace the Animal class on the right side with an interface.
- Pros: Code re-use, easier to understand
- Cons: Tightly coupled, fragile, prone to be abused by developers
- Pros: Code re-use, great Flexibility, Loose coupling
- Cons: A little harder to understand, complex design structure
Here, I have explained in details of the types of object relationships various examples illustrating Association, Aggregation and Composition and Inheritance in Java. Also, I have briefed about the scenarios where you have to take a design decision whether to go for composition or inheritance. I believe this article gave you enough information about modeling your application design in choosing aggregation or composition or inheritance concepts.