Be careful with generics!

In my previous post, I’ve dealt with the risk of primitives. In this post I will show a similar theme, the risk of applying generics repeatedly in too much depth.
In the following example, we have a software system which deals with the products of different companies, where the products are grouped into product families. One functionallity of the system is to give a purchase report, which has to be presented on the user interface in the following form:
Company1 Average Sells By Day Average Age Of Customer -- Product Family A ---- Product1 115 28 ---- Product2 987 47 -- Product Family B ---- Product5 211 32 Company2 -- Product Family B ---- Product3 111 19 ...
We have a data structure in the software system for representing a purchase report. It is based on the standard containers of the language (the example is based on the Java language):
Map < String, Map < String, Map < String, Map < String, int > > > > purchaseReport;
I don't know who thinks that this structure is easy to understand? I suppose nobody thinks that, so far, the original author couldn’t easily understand this structure two months after he coded this! Instead of using repeatedly the standard containers of the actual programming language, we should define our own types with meaningful names. In this sense, we have to define the product type for the purchase report first:
class ProductPurchaseReport{
String productName;
Map < String, int > statistics;
}
By defining ProductPurchaseReport class, the developer has the opportunity to take further data in this class which are not appearing in the report, but could be required in other cases. For example, the original Product object can be aggregated into this class, instead of the product name, and by this way the product object can be accessed immediately if it is needed:
class ProductPurchaseReport{
Map < String, int > statistics;
Product product;
//... getters and setters and other methods
}
Following the original data structure, I define the product family class for the purchase report with a reference to the original product family object (similarly to ProductPurchaseReport):
class ProductFamilyPurchaseReport{
Map < String, ProductPurchaseReport > productPurchaseReports;
ProductFamily productFamily;
... getters and setters and other methods
}
In the next step, I define the company for the purchase report:
class CompanyPurchaseReport{
Map < String, ProductFamilyPurchaseReport > productFamilyPurchaseReports;
Company company;
... getters and setters and other methods
}
Finally, we can define the PurchaseReport type:
class PurchaseReport{
Map < String, CompanyPurchaseReport > companiesPurchaseReports;
... getters and setters and other methods
}
By introducing these classes, the code became more easily understandable, and furthermore, it is easier to maintain. I have to make a note that there are several other potential solutions for this problem, like introducing a composite design pattern:
//Component participant of Composite pattern
class PurchaseReportComponent{
}
//Composite participant of Composite pattern
class PurchaseReportComposite{
ArrayList < PurchaseReportComponent > children;
}
// Specific composite participant of Composite pattern
class ProductFamilyPurchaseReport : PurchaseReportComposite{
ProductFamily productFamily;
... getters and setters and other methods
}
// Specific composite participant of Composite pattern
class CompanyPurchaseReport : PurchaseReportComposite {
Company company;
... getters and setters and other methods
}
// Leaf participant of Composite pattern
class ProductPurchaseReport : PurchaseReportComponent {
Product product;
Map < String, int > statistics;
}
This solution is more flexible than the first, because it also enables to build the report where product families are preceding companies; it just depends on the builder of this composite pattern:
Product Family A Average Sells By Day Average Age Of Customer -- Company1 ---- Product1 115 28 …
To sum up, by introducing classes, the original structure representing the purchase reports became more easily understandable, and this way it became more easily maintainable. So, my final advice is, just be careful with applying generics repeatedly!












