Сила метаданных: как работать со спагетти-кодом

Мы все пытаемся использовать общие подходы и известные шаблоны для создания приложения с минимальными усилиями и максимальной отдачей. У нас есть отличные библиотеки и мощные фреймворки, которые выполняют рутинные операции за нас. Все это мы используем для того, чтобы сосредоточиться только на бизнес-логике. Однако эта погоня довольно часто приводит нас к спагетти-коду, особенно когда речь идет о реализации функции без готового решения для нее. В этой статье я хочу поделиться с вами одним мощным инструментом, который, по моему опыту, ценят не все разработчики. Этот инструмент есть в большинстве языков программирования, и он очень часто используется во многих фреймворках — аннотации. Кофе-брейк #88. Сила метаданных: как работать со спагетти-кодом. Сборка мусора в Java — как она работает и в чем ее преимущества - 1

Вы любите спагетти?

Давайте рассмотрим пример, с которым я столкнулся пару лет назад. Мне нужно было сделать синтаксический анализ электронной таблицы Excel, чтобы поместить проанализированные данные в базу данных. Также я хотел собрать часть данных из базы данных и создать электронную таблицу. Для реализации я использовал известную Java-библиотеку — Apache POI. API библиотеки облегчает работу, поскольку позволяет вручную создавать лист, строку, ячейку и другие элементы. Это очень хорошо, но когда необходимо генерировать различные электронные таблицы Excel, код становится абсолютно нечитаемым и неподдерживаемым. В итоге, как это обычно бывает, первая версия приложения получается просто ужасной. Реализация состояла из класса данных, который представлял строку со всеми полями, необходимыми для синтаксического анализа. Также был парсер, в котором поля Excel анализировались ячейка за ячейкой и помещались во вновь созданный экземпляр класса данных. Поначалу программа работала отлично и делала то, что от нее требовалось. Проблемы начались, когда пришло время вносить какие-то модификации; код не читался. Даже я, написавший этот код, не мог найти подходящего места для размещения новых строк для реализации новой необходимой мне функции.

Спасение в аннотациях

Спасли приложение от этого спагетти-кода аннотации. Чтобы избавиться от неподдерживаемого кода, мне нужно было перенести логику определения того, какой столбец следует анализировать, какой тип данных содержится в ячейке и все остальное в другое место. Для этого я создал аннотацию, в которой указал имя столбца для каждого поля класса. В аннотации я также добавил переменную, с помощью которой можно выбирать цвет и шрифт ячейки. Тем самым код в классе синтаксического анализа был значительно сокращен. Только один обработчик динамически создавал электронную таблицу по параметрам, взятым из аннотаций. Это была победа. Затем, чтобы внести какие-либо изменения в приложение, мне просто нужно было создать класс с аннотациями. Решение напоминало библиотеку Jackson, которая анализирует JSON с помощью аннотаций, и, я думаю, нет необходимости рассказывать, насколько удобна Jackson или аналогичные библиотеки.

@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);
По мере развития приложение получило новую аннотацию, с помощью которой в электронной таблице можно было создать ячейку с функцией внутри. Различные поля можно умножать, вычитать, использовать любые общие функции Excel. Также я добавил ​​итоговую строку для отображения суммы по столбцу. И все это было я сделал лишь за счет незначительной модификации основного парсера и простого добавления аннотаций к классам.

@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() + ")";
    }
Источник: Hackernoon