4 ms·
I actually have a concrete example that I've been working on this week. At work we have a java service that creates an Excel workbook with several tabs. When th
by anyonecancode 8y ago
I actually have a concrete example that I've been working on this week. At work we have a java service that creates an Excel workbook with several tabs. When the original authors of the code wrote the service, the specific tabs it needed to generate were fixed, so they just wrote the logic for writing the tabs directly into the method that processes the data, e.g. this general pattern:
public Map<String, someDataHolder> inheritedFormatData () {
Map<String, someDataHolder> formattedData = new ArrayList<>();
for (Record record : records) {
formattedData.put('tabA', processTabA(record));
formattedData.put('tabB', processTabB(record));
formattedData.put('tabC', processTabC(record));
formattedData.put('tabD', processTabD(record));
}
}
And that works well enough, until there was a need for having variations. In that case they just subclassed this formatter class and provided an override for "proccessTabB" for example.
The problem is that this week, I've been tasked with adding a new tab, tabE. There's no "processTabE" method to override. I basically have a few unpalatable choices:
- Directly add something like "formattedData.put('tabE', processTabE(record));", and the method "processTabE", to the base class. The danger here is that I have to make sure I don't accidentally break some behavior in one of the dozens of child classes (there's more complex inheritance than I'm sharing in this simple example).
- Inherit, then add another method that loops through "records" again and only processes tabE. That's safer from the point of view of inheritance, but it's very wasteful to loop through all the records more than once (there could be as many as a million in my particular case).
- Copy-paste the base class code into a new class. That solves the above problems but, ugh.
Consider a different approach:
public Map<String, someDataHolder> composedFormatData (List<Tabs> tabs) {
Map<String, someDataHolder> formattedData = new ArrayList<>();
records.forEach(record -> tabs.forEach(tab -> tab.process());
tabs.forEach(tab -> {
String mapKey = tab.getKey();
formattedData.put(mapKey, tab.getProcessedData());
})
}
If we define a "Tab" type, with method for storing a key, processing and storing records, and providing that processed data, we can create a list of as many of these tabs as we want, and define each tab's manner for processing the data. Adding a variation to the report generation class is now a matter of defining a different list of tabs and passing that in. No more inheritance woes, plus it nudges you in the direction of better encapsulation of your state (ie each tab is responsible for itself, rather than that logic living in some base super class). Hope this concrete example helps.