jeudi 21 janvier 2021

How to apply Single Principle Responsibility in Java

I have a service that converts a xls file into html. It is working just fine, but it is quite a big method that doesn't follow any SOLID principles. Therefore I would like to improve it to follow at least the Single Responsibility Principle. But I really don't know how to apply it and find the level of abstraction in my case.

@Service
public class xlsToHtmlImpl implements MultipartFileToHtmlService {

private final HtmlLayout htmlLayout;

@Autowired
public xlsToHtmlImpl(HtmlLayout htmlLayout) {
    this.htmlLayout = htmlLayout;
}


@Override
public InputStream multipartFileToHtml(MultipartFile multipartFile, boolean hasOnlyOneSheet, boolean hasBorders) throws IOException {

    String fileName = multipartFile.getOriginalFilename();
    BufferedInputStream inputStream = new BufferedInputStream(multipartFile.getInputStream());

    Workbook workbook;
    assert fileName != null;
    //Selecting workbook depending on FileType
    if (fileName.toLowerCase().endsWith(htmlLayout.FILE_TYPES[0])) {
        workbook = new HSSFWorkbook(inputStream);
    } else {
        workbook = new XSSFWorkbook(inputStream);
    }
    //Writing content of multipartFile to outputstream
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    outputStream.write(htmlLayout.openStyle());

    //Selecting style to apply depending on user input
    if (hasBorders) {
        outputStream.write(htmlLayout.noBordersStyle());
    } else {
        outputStream.write(htmlLayout.withBordersStyle());
    }
    outputStream.write(htmlLayout.closeStyle());

    outputStream.write(htmlLayout.openNewHtml());
    outputStream.write(fileName.getBytes());


    //Different algorithm for the content of the body depending on user input
    Sheet sheet;
    if (hasOnlyOneSheet) {
        sheet = workbook.getSheetAt(0);
        Iterator<Row> rows = sheet.rowIterator();
        while (rows.hasNext()) {
            Row row = rows.next();
            Iterator<Cell> cells = row.cellIterator();
            outputStream.write(htmlLayout.newLine());
            outputStream.write(htmlLayout.newRow());
            while (cells.hasNext()) {
                Cell cell = cells.next();
                outputStream.write(htmlLayout.newCell());
                outputStream.write(cell.toString().getBytes());
                outputStream.write(htmlLayout.closeCell());

            }
            outputStream.write(htmlLayout.closeRow());
        }
    } else {
        for (int i = 0; i< workbook.getNumberOfSheets(); i++) {
            sheet = workbook.getSheetAt(i);
            Iterator<Row> rows = sheet.rowIterator();
            outputStream.write(htmlLayout.newLine());
            outputStream.write(htmlLayout.newRow());
            outputStream.write(htmlLayout.closeCell());
            outputStream.write(htmlLayout.closeRow());
            outputStream.write(htmlLayout.newLine());
            while (rows.hasNext()) {
                Row row = rows.next();
                Iterator<Cell> cells = row.cellIterator();
                outputStream.write(htmlLayout.newLine());
                outputStream.write(htmlLayout.newRow());
                while (cells.hasNext()) {
                    Cell cell = cells.next();
                    outputStream.write(htmlLayout.newCell());
                    outputStream.write(cell.toString().getBytes());
                    outputStream.write(htmlLayout.closeCell());

                }
                outputStream.write(htmlLayout.closeRow());
            }
        }
    }

    outputStream.write(htmlLayout.newLine());
    outputStream.write(htmlLayout.closeHtml());
    outputStream.close();

    //Returning result as ByteArrayInputStream to controller
    return new ByteArrayInputStream(outputStream.toByteArray());
}

Where htmlLayout contains html snippet like:

public byte[] closeHtml() {return "</table></body></html>".getBytes();}

I tried to follow this article: https://www.baeldung.com/java-single-responsibility-principle#:~:text=As%20the%20name%20suggests%2C%20this,only%20one%20reason%20to%20change.&text=These%20classes%20are%20harder%20to%20maintain.

Following this article, I tried to create different classes as follow:

public class HtmlStyleWrapper {

private byte[] style;


public byte[] withBordersStyle() {
    return ("table, td{" +
            "    border: 1px solid black;\n" +
            "    border-collapse: collapse;\n" +
            "    padding: 9px;\n" +
            "}").getBytes();
}

public byte[] noBordersStyle() {
    return ("td {" +
            "    padding: 9px;\n" +
            "}").getBytes();
}

public byte[] openStyle() {
    return "</title></head><body><style>".getBytes();
}

public byte[] closeStyle() {
    return "</style><table>".getBytes();
}

public void wrapStyle(ByteArrayOutputStream outputStream, boolean hasBorders) throws IOException {
    outputStream.write(openStyle());
    if (hasBorders) {
        outputStream.write(noBordersStyle());
    } else {
        outputStream.write(withBordersStyle());
    }
    outputStream.write(closeStyle());
}


public class HtmlBodyWrapper {

private byte[] body;

public byte[] openNewHtml() {
    return "<!DOCTYPE html><html><head><title>".getBytes();
}

public byte[] newLine() {
    return "\n".getBytes();
}

public byte[] closeHtml() {
    return "</table></body></html>".getBytes();
}

public byte[] newRow() {
    return "<tr>".getBytes();
}

public byte[] closeRow() {
    return "</tr>".getBytes();
}

public byte[] newCell() {
    return "<td>".getBytes();
}

public byte[] closeCell() {
    return "</td>".getBytes();
}

public void wrapBody(ByteArrayOutputStream outputStream, String fileName, boolean hasOnlyOneSheet, Workbook workbook) throws IOException {
//Write to outputstream
}

The aim would be to get something like

wrapHTMLBody(wrapStyle(htmlLayout.getHTML_STYLE()), table)

But I feel like I'm not taking the right approach and that I didn't understand correctly SRP.

Aucun commentaire:

Enregistrer un commentaire