Understanding Composition
In an earlier article, we learned about how JAVA implements the concept of inheritance, to create class hierarchies https://medium.com/@konlanmikpekoah.km/understanding-inheritance-using-java-bbe184bc824b.
In this example, we’ll see how we can use a concept called composition, to create complex objects out of simpler ones. So you might recall that the inheritance model is a type of relationship.
So here in this class diagram, a book object is a publication because it inherits from the publication base class, and picks up all the attributes and methods from the base class as well. So we can also say that magazine is a periodical, and we can see that magazine is a publication because again, it has the same common base class.
Composition works a little differently. When using composition, we build objects out of other objects, and this model is more of a has relationship.
So in the diagram above, the book object, has an author object, which contains information about the author. Rather than defining all of the author-related information, directly within the book class hierarchy. This type of model lets us extract distinct ideas, and put them into their own classes. Now, inheritance and composition are not exclusive, you can combine both depending on what the needs of your application are. So let’s apply this concept in some real code, to see how it works.
Book.java
import java.util.ArrayList;
import java.util.List;
public class Book {
private String title;
private double price;
private String authorFname;
private String authorLname;
List<String> chapters = new ArrayList();
public Book(String title, double price, String authorFname, String authorLname) {
this.title = title;
this.price = price;
this.authorFname = authorFname;
this.authorLname = authorLname;
}
public void addChapter(String chapterName){
chapters.add(chapterName);
}
}
And I’ve defined a book class, there’s the title and the price, along with some author information, the first and last name, and there’s an attribute to hold the list of chapter information. There’s also a method to add chapters to the book, and it takes the name of the chapter and add it to the array list. Now, this particular class definition is all fine and good, but it’s pretty monolithic.
There are pieces of information like the author, and maybe the chapter information that might make sense to treat as separate entities. It’s not hard to imagine a scenario where we might want to work with just a group of authors or get information about specific book chapters. So we can use composition to separate these discrete pieces of information from the overall book object. So let’s start by extracting the author information into its own class.
Author.java
public class Author {
private String firstName;
private String lastName;
public Author(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
So then I have to modify the book class, to take an author object as an argument in the constructor.
Book.java
import java.util.ArrayList;
import java.util.List;
public class Book {
private String title;
private double price;
private Author author;
List<String> chapters = new ArrayList();
public Book(String title, double price, Author author) {
this.title = title;
this.price = price;
this.author = author;
}
public void addChapter(String chapterName){
chapters.add(chapterName);
}
}
So now, we’ve created a relationship where a book has an author associated with it, instead of keeping that implementation details of the author data, wrapped up within the book class.
So let’s go ahead and create a separate class for the chapters.
we have even added the pageCount for every chapter in the chapter class.
public class Chapter {
private String name;
private int pageCount;
public Chapter(String name, int pageCount) {
this.name = name;
this.pageCount = pageCount;
}
}
we’ve now created a relationship where a book has a collection of chapter objects. We can even add a new method, and we’ll call this get book page count, whereby the book can calculate its own page count
import java.util.ArrayList;
import java.util.List;
public class Book {
private String title;
private double price;
private Author author;
List<Chapter> chapters = new ArrayList();
public Book(String title, double price, Author author) {
this.title = title;
this.price = price;
this.author = author;
}
public void addChapter(Chapter chapter){
chapters.add(chapter);
}
public int totalPageNumber(){
int result = 0;
for( Chapter chapter: chapters){
result += chapter.getPageCount();
}
return result;
}
}
Okay, so now we have some nice separation of responsibilities. So for example, printing the full name of the author is done within the author class. So we can print out the author of the book, and we’ll leave the title alone. And calculating the book size is done by using the data, that’s in the chapter class. We’ve got our extracted chapter and author objects, and we’ve updated the book class to use that data instead.
in Conclusion
So what we’ve done is taken a monolithic class definition, and made it more extensible and flexible, by composing it from simpler class objects, each of which is responsible, for its own features and data.