Është jashtëzakonisht e zakonshme të hasim metoda në klasa që janë mjaft të mëdha, komplekse ose të dyja. Sa i madh është shumë i madh? Këtu nuk ka përgjigje përfundimtare, por një gjë është e qartë: e dini kur e shihni!

Disa tregues të aromave të mëdha të metodës janë: kompleksiteti i lartë ciklomatik (të themi ›15), trupi i metodës nuk shihet brenda dritares së redaktuesit (dhe zvogëlimi i madhësisë së shkronjave për të parë të gjithë metodën brenda dritares së redaktuesit sigurisht që nuk është një rregullim!), shumë kthime deklarata, folezim i thellë (›5 nivele)…

Këtu është një shembull i një metode komplekse:

private static void generatePollutantwisePollutionReport(Map<String, List<PollutantDetailsWithCity>> pollutantwisePollutantEntriesForReport, LocalDateTime generatedTime) throws Exception {
    try {
        List<String> headers = Arrays.asList("Average", "Max", "Min", "City");
        Document document = new Document();
        PdfWriter.getInstance(document, new FileOutputStream("PollutantwisePollutionReport.pdf"));
        document.open();
        document.add(new Paragraph("India Pollution Report - Pollutant-wise", catFont));
        document.add(new Paragraph("Generated on: " + generatedTime, smallBold));

        for(String tableHeader : pollutantwisePollutantEntriesForReport.keySet()) {
            Paragraph paragraph = new Paragraph(tableHeader);
            PdfPTable table = new PdfPTable(headers.size());
            for(String rowHeader : headers) {
                PdfPCell pdfPCell = new PdfPCell(new Phrase(rowHeader));
                table.addCell(pdfPCell);
            }
            for (PollutantDetailsWithCity tableEntries : pollutantwisePollutantEntriesForReport.get(tableHeader)) {
                table.addCell(String.valueOf(tableEntries.average));
                table.addCell(String.valueOf(tableEntries.max));
                table.addCell(String.valueOf(tableEntries.min));
                table.addCell(String.valueOf(tableEntries.city));
            }
            paragraph.add(table);
            document.add(paragraph);
        }
        document.close();
    } catch(Exception e) {
        System.err.println(e);
    }
}

Ju madje mund të debatoni nëse kjo është fare komplekse. Por për mua është sigurisht komplekse. Çdo metodë duhet të jetë vetëm disa rreshta kodi (‹ 10 rreshta) dhe të ketë pak kompleksitet ciklomatik (rreth vetëm 3–4)!

Tani, le të përqendrohemi në atë që bën kjo metodë:

a) Gjëja e parë që vërejmë është se është private dhe statike. Pra, është një metodë e dobishme për t'u përdorur brenda klasës.

b) Emri & parametrat tregojnë se po gjeneron një raport nga të dhënat e dhëna të kaluara si argumente.

c) Nga trupi i madh i metodës, shohim se pjesët fillestare të metodës formojnë hyrjet për krijimin e një tabele PDF dhe pjesët e mëvonshme të metodës plotësojnë përmbajtjen e metodës dhe gjenerojnë një raport PDF.

d) Ekziston dyfishim i kodit të nivelit të deklaratës, duke treguar se abstragimi i argumenteve mund ta bëjë atë më konciz:

table.addCell(String.valueOf(tableEntries.average));
table.addCell(String.valueOf(tableEntries.max));
table.addCell(String.valueOf(tableEntries.min));
table.addCell(String.valueOf(tableEntries.city));

Sa herë që hasni në një erë (në këtë rast erë me metodë të madhe), pyetja e parë që duhet të bëjmë për adresimin e erës është: cili është parimi themelor që shkel kodi?

Në këtë rast, përgjigja është se ai shkel parimin e ndarjes së shqetësimeve: kodi përzien logjikën e biznesit në lidhje me detajet e raportit që do të krijohen me detajet aktuale të mprehta që lidhen me gjenerimin e raportit PDF.

Hapi drejt rifaktorimit është kryesisht ndarja e logjikës së krijimit të PDF nga logjika e biznesit. Pasi të adresohet era e dizajnit, ne kemi pak aromë kodi për të adresuar, të tilla si përsëritja e jashtme (cithe for) dhe dyfishimi i kodit (ato thirrje të përsëritura në metodën table.addCell).

Le të nxjerrim së pari një klasë të ripërdorshme të quajtur PDFReport që abstrakton përgjegjësinë në lidhje me krijimin e një raporti PDF:

public class PDFDocument implements AutoCloseable {
    private String fileName;
    private Document document;
    private static Font catFont = new Font(Font.FontFamily.TIMES_ROMAN, 18,
            Font.BOLD);
    private static Font smallBold = new Font(Font.FontFamily.TIMES_ROMAN, 12,
            Font.BOLD);

    public PDFDocument(String fileName) throws IOException, DocumentException {
        this.fileName = fileName;
        document = new Document();
        PdfWriter.getInstance(document, new FileOutputStream(fileName));
        document.open();
    }

    public void addTitle(String title) throws DocumentException {
        document.add(new Paragraph(title, catFont));
    }

    public void addHeader(String header) throws DocumentException {
        document.add(new Paragraph(header, smallBold));
    }

    public void addTable(String title, List<String> headers, List<String> entries)
            throws DocumentException {
        PdfPTable table = new PdfPTable(headers.size());

        headers.forEach(header -> addTableCell(table, header));
        entries.forEach(entry -> table.addCell(entry));

        Paragraph paragraph = new Paragraph(title);
        paragraph.add(table);
        document.add(paragraph);
    }

    // Helper method to add a table cell with the given header value
    private void addTableCell(PdfPTable table, String header) {
        PdfPCell c1 = new PdfPCell(new Phrase(header));
        c1.setHorizontalAlignment(Element.ALIGN_CENTER);
        table.addCell(c1);
    }

    @Override
    public void close() throws Exception {
        document.close();
    }
}

Dhe tani kodi që lidhet me logjikën e biznesit të gjenerimit të një raporti ndotës:

public class PollutantReport {
    private String heading;
    private LocalDateTime dateTime;
    public PDFReport(String heading, LocalDateTime dateTime) {
        this.heading = heading;
        this.dateTime = dateTime;
    }

    public void generate(String fileName, Map<String, List<String>> entries, List<String> columnHeaders) throws Exception {
        try (PDFDocument report = new PDFDocument(fileName)) {
            report.addTitle(heading);
            report.addHeader("Generated at: " + dateTime);
            entries.forEach((tableTitle, values) -> {
                addTableToReport(columnHeaders, report, tableTitle, values);
            });
        }
    }

    private void addTableToReport(List<String> columnHeaders, PDFDocument report, String tableTitle, List<String> values) {
        try {
            report.addTable(tableTitle, columnHeaders, values);
        } catch (DocumentException e) {
            e.printStackTrace();
            System.exit(-1);
        }
    }
}

Me këtë rifaktorim, ne kemi krijuar dy abstraksione: një për gjenerimin e raporteve PDF dhe një tjetër për logjikën e biznesit për krijimin e raporteve të ndotësve. Të dyja nuk kanë ndonjë aspekt konkret të lidhur me të - p.sh., titulli "Raporti i ndotjes së Indisë - nga pikëpamja e ndotësve"do të jetë në kodin e klientit të klasës. Kështu duhet të jetë!

Oh po, e dëgjoj atë që po thua: "Ganesh, shiko çfarë bëre - ke ndarë një metodë të madhe në dy klasa dhe kjo është më shumë linja kodi! Kaq shumë flitet për shkrimin e kodit konciz dhe ajo që bëni është të shkruani një kod më të gjatë!” Vëzhgim i mirë, vlerësojeni. Megjithatë, vini re se abstraksionet që rezultojnë janë të ripërdorshme tani dhe ato zhvillohen të pavarura nga njëra-tjetra. Për shembull, nëse aspektet e raportimit PDF ndryshojnë ose nëse biblioteka PDF duhet të zëvendësohet me diçka tjetër, atëherë vetëm kodi në klasën PDFDocument duhet të ndryshojë dhe asgjë tjetër. Dhe nëse gjenerojmë një raport të ndryshëm me vlera të ndryshme, atëherë ndryshon vetëm ajo logjikë e biznesit në klasën PollutantReport. Prandaj, ky është një kod shumë më i ripërdorshëm.

Me fjalë të tjera, kur e bëni kodin të ripërdorshëm, shpesh, kodi juaj rritet në madhësi sesa versioni i koduar, pikërisht për faktin se po e bëni atë gjenerik! Ky është çmimi që ne paguajmë për kodin e ripërdorshëm dhe unë do të thosha se ia vlen ta paguajmë çmimin.

Përmbledhje:Nuk ka asnjë strategji të vetme për rifaktorimin e një metode të madhe. Në këtë postim në blog, ne diskutuam një shembull specifik të një metode të madhe dhe se si ajo ka erë "shqetësime të përziera". Ne rifaktoruam duke nxjerrë dy klasa nga kodi dhe ndamë detajet konkrete në kodin e klientit. Rezultati është një dizajn që është i zgjerueshëm dhe i ripërdorshëm.

(Shkruar nga Ganesh Samarthyam, Bashkëthemelues në KonfHub Technologies LLP)