18. Сделать рефакторинг обработки документов
Условие задачи:
Дан сервис обработки документов разных типов. Нужно выполнить рефакторинг, убрать разрастающийся switch и упростить добавление новых форматов.
Код:
public class RefEx {
public enum DocumentType {
XML,
PDF,
DOCX
}
public static class Document {
String id;
DocumentType type;
String content;
}
public static class DocumentService {
public void process(Document[] d) {
for (Document i : d) {
// Общая логика обработки документа
switch (i.type) {
case DocumentType.PDF: {
// Специфическая логика для обработки PDF
} break;
case DocumentType.DOCX: {
// Специфическая логика для обработки Word
} break;
case DocumentType.XML: {
// Специфическая логика для обработки XML
} break;
}
}
}
}
}
Спойлеры к решению
Подсказки
d и i не отражают смысл переменных.💡 Нужно проверить массив и его элементы на
null.💡 При добавлении нового типа придётся изменять
DocumentService.💡 Специфическую обработку лучше вынести в отдельные классы.
💡 Для выбора обработчика можно использовать паттерн Strategy.
Решение
Проблемы:
DocumentServiceсодержит логику обработки всех форматов;при добавлении нового типа нужно изменять
switch— нарушается OCP;общая и специфическая логика смешаны;
имена
dиiплохо отражают назначение переменных;нет проверки массива, документов и
typeнаnull;для enum в обычном
switchcase следует писатьcase PDF, а неcase DocumentType.PDF;при большом количестве форматов
switchбудет постоянно разрастаться.
Лучше применить Strategy и вынести обработку каждого формата в отдельный класс.
public interface DocumentProcessor {
DocumentType getType();
void process(Document document);
}
Например:
public class PdfDocumentProcessor implements DocumentProcessor {
@Override
public DocumentType getType() {
return DocumentType.PDF;
}
@Override
public void process(Document document) {
// Специфическая логика обработки PDF
}
}
Аналогично создаются DocxDocumentProcessor и XmlDocumentProcessor.
Сам сервис отвечает только за выбор стратегии и общую обработку:
public class DocumentService {
private final Map<DocumentType, DocumentProcessor> processors;
public DocumentService(
Collection<DocumentProcessor> processors
) {
this.processors = new EnumMap<>(DocumentType.class);
for (DocumentProcessor processor : processors) {
DocumentProcessor previous =
this.processors.put(
processor.getType(),
processor
);
if (previous != null) {
throw new IllegalArgumentException(
"Duplicate processor: "
+ processor.getType()
);
}
}
}
public void process(Document[] documents) {
if (documents == null) {
throw new IllegalArgumentException(
"Documents must not be null"
);
}
for (Document document : documents) {
if (document == null || document.type == null) {
throw new IllegalArgumentException(
"Invalid document"
);
}
// Общая логика обработки документа
DocumentProcessor processor =
processors.get(document.type);
if (processor == null) {
throw new IllegalArgumentException(
"Unsupported document type: "
+ document.type
);
}
processor.process(document);
}
}
}
Теперь для нового формата достаточно добавить новый DocumentProcessor. DocumentService изменять не требуется.
Основная идея рефакторинга: общая логика остаётся в сервисе, а специфическая логика каждого формата переносится в отдельную Strategy.