The Power of Metadata: How to Deal with Spaghetti Code

Written by sergeidzeboev | Published 2021/08/03
Tech Story Tags: java-annotations | annotations | metadata | java | power | annotations-power | spaghetti-code | coding

TLDR Most programming languages have this tool, and it is a very often used tool in many frameworks - annotations. In this article, I want to share with you one powerful tool that, in my experience, not all developers value. The solution that saved the application from this spaghetti code was annotations. Annotations are not the fastest solution in terms of execution time, it uses reflection to get parameters in runtime. But does the game worth it today when we have very powerful hardware? This question is for each of you to decide we would prefer a couple of seconds faster or readable and easy to support solution.via the TL;DR App

Nowadays, we are trying to use common approaches and well-known patterns to create an application with minimum effort and maximum value.
We have great libraries and powerful frameworks that do routine operations for us. All that we are using in order to focus only on business logic. However, this chasing quite often leads us to spaghetti code, especially when it comes to implementing a feature without any ready solution for it.
In this article, I want to share with you one powerful tool that, in my experience, not all developers value. Most programming languages have this tool, and it is a very often used tool in many frameworks - annotations.

Do you like spaghetti?

Let's look into an example that I bumped into a couple of years ago. I needed to implement excel spreadsheet parsing to put the parsed data into the database and vice versa - I needed to collect some data from DB and create a spreadsheet.
In order to implement it, I used a well-known java library - Apache POI. API of the library allowed to work on a low level and create a sheet, row, cell and etc., manually, which is very flexible, but code becomes absolutely not readable and unsupportable when it is needed to generate different excel spreadsheets. Since, as it usually happens, I needed to implement the feature in a short period of time, the first version of the application was terrible.
The implementation consisted of a data class which was a representation of a row with all the fields that are needed to parse. And a parser where the excel fields were parsed cell by cell and were put to the newly created instance of the data class. Nevertheless, the program was working perfectly and did what it needed to do. Problems started when the time to add some modifications came; the code was not readable. Even I, who wrote the code, couldn't find the right place to put the new lines to implement the new feature that I needed.

Annotations. The rescuers

The solution that saved the application from this spaghetti code was annotations. To get rid of the unsupportable code, I needed to put the logic of defining what column should be parsed, what data type contains in the cell, etc., to another place. And of course, the best approach here was to put this responsibility into the entity itself. I have created an annotation where I could put the name of the column for each field of the class.
In the annotation, I also added a variable by which I could choose the color and font of the cell. By that, the code in the parsing class was drastically reduced. It was only one handler that dynamically created a spreadsheet by parameters taken from the annotations. It was a victory. Then to add any modification to the application, I just needed to create a class with the annotations. The solution worked pretty much similar to Jackson library, which parses a JSON using annotations, and there is no need, I think, to tell how convenient Jackson or similar libraries are.
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnExcel {

    String name() default "";

    int position();

    ExcelColumnDataFormat cellTypePattern() default ExcelColumnDataFormat.NONE;

    IndexedColors cellColor() default IndexedColors.AUTOMATIC;

    ExcelTotalFormula total() default ExcelTotalFormula.NONE;

}
ColumnExcel columnExcel = field.getAnnotation(ColumnExcel.class);
With evolving, the application got a new annotation using which a cell with a function inside could be created in the spreadsheet. The different fields could be multiplicated, subtracted and any common excel functions could be used in the generated spreadsheet. Also, the summary row has been added to show the sum by a column. And all that was done only by slightly modifying the main parser and just adding annotations to the classes.
@ColumnExcel(
            name = "Views",
            position = 4,
            total = ExcelTotalFormula.SUM)
    private BigDecimal variableC;

    @ColumnExcelFormula(
            name = "Conversion",
            position = 5,
            cellTypePattern = CellDataTypeFormatPattern.PERCENTAGE
    )
    public String variableD(int rowNumber) {
        return new CellAddress(rowNumber, 4).formatAsString() + "*" 
		+ new CellAddress(rowNumber, 2).formatAsString();
    }

    @ColumnExcelTotalFormula(position = 4, cellTypePattern = CellDataTypeFormatPattern.RUR)
    public static String getVariableCTotalFormula(int firstRowNum, int lastRowNum) {
        return "SUM( " + new CellAddress(firstRowNum, 4).formatAsString() + ":" 
		+ new CellAddress(lastRowNum, 4).formatAsString() + ")";
    }

Conclusion

With metadata, the code became more readable and much easier to support. Of course, annotations are not the fastest solution in terms of execution time. It uses reflection to get parameters in runtime, which consumes more time than the straightforward implementation.
But does the game worth it today when we have very powerful hardware? This question is for each of you to decide we would prefer a couple of seconds faster or readable and easy to support solution.

Written by sergeidzeboev | Senior Java Developer
Published by HackerNoon on 2021/08/03