Disclaimer: The content of this blog are my views and understanding of the topic. I do not intend to demean anything or anyone. I am only trying to share my views on the topic so that you will get a different thought process and angle to look at this topic.
Java Collections Framework is solving most of our problems with data processing but at times we have to rely on third-party libraries for some features which are missing. But adding a new library to the project only for using some of the features is not a good practice. We should choose a library when we are going to use all of its features. One such need was when I wanted to perform a simple operation on a collection wherein, I need to check if an element exists in a collection, then update it and if not then add it. The below blog is about how to add this feature by extending the java collections.
Inheritance vs Composition
Although there are more supporters who advocate preferring Composition over Inheritance, I prefer to be more pragmatic. A hammer-and-nail situation should be avoided whenever you are choosing an approach. In our case, I chose inheritance since the benefit of inheritance is that I get all the features of the parent and could easily blend in without much fuss. Let's dive into the code.
Problem Statement
Merge if present and add if not present
A typical CRUD app requires us to update the record. Now consider a nested object which is modeled as a Set and we need to update a value in a set such that if a record already exists then update specified fields and if not then add it to the list.
We will be using the id property as a unique identifier for the person for hashcode and equals implementation.
{
"access": "online",
"persons": [
{
"id": 1,
"name": "John"
}
]
}
In the above example, we need to change the name of the person if exists. If the person entry is not present in the Set then add it. Let us extend the HashSet and implement this using Lambda style.
import java.util.HashSet;
import java.util.Objects;
import java.util.function.Consumer;
public class EnhancedCollection<T> extends HashSet<T> {
public void merge(T givenItem, Consumer<T> updateFunction) {
stream().filter(collectionItem -> Objects.equals(collectionItem, givenItem)).findAny()
.ifPresentOrElse(updateFunction, () -> add(givenItem));
}
}
Code Analysis
In the above code, we extended the HashSet with Java generics in place so that we can use any Object. We initially find the record and if present we call the update function which can be applied to that record using a higher order function programming or else, we call an add function. We are leveraging a new feature of Java Optional introduced in Java 9. Now you can simply treat this class as an EnhancedSet which can perform all operations of a set with an additional merge operation. However, imagine you are going with Composition in which case we can make the EhnancedCollection class as a Decorator following the Decorator Design pattern then you need to implement the set interface and delegate the calls to the backing set class. Which is an overhead in this case.
Summary
Although reusability is good for software development it should be treated like an accessory and not a mandate. Since at times for a single feature like this, we should not end up adding a library that performs all the collections operations which you don't use in your project. Also whenever working on a code just think if we could generalize it for more reusability.
Comments