java void 函数式编程、紧耦合和样板

java void functional programming, tight coupling and boilerplate

当目标对象 excelColumnspdfColumns 部分共享相同的对象,其中一些甚至有条件地共享时,什么是好的 OOP 模式来避免函数式编程、紧耦合和样板代码,如下面的代码所示?让我们假设,将会有很多共享列,只有少数非共享列和条件列。

    List<Column> excelColumns = new ArrayList<>();
    List<Column> pdfColumns = new ArrayList<>();

    //shared columns
    Column test = new Column("test", 121, 11);
    excelColumns.add(test);
    pdfColumns.add(test);

    //conditional columns
    if (condition) {
        excelColumns.add(new Column("test2", 12, 21));
    }

    //non shared columns
    pdfColumns.add(new Column("test3", 12, 41));

    //shared columns
    Column test4 = new Column("test4", 12, 331);
    excelColumns.add(test4);
    pdfColumns.add(test4);
    Column test5 = new Column("test5", 72, 11);
    excelColumns.add(test5);
    pdfColumns.add(test5);
    Column test6 = new Column("test6", 82, 121);
    excelColumns.add(test6);
    pdfColumns.add(test6);

您可以对最后一个共享列部分使用 addAll(...) 方法而不是 add(...) 来为每个集合添加它们。如果您的目标是维护插入下一列的条件顺序,则没有必要对其进行混淆,因为它已在此处清楚地呈现。

根据您对复杂性的偏好,您可以执行以下操作:

  1. 尝试将相关的列实例分组到对象中,例如 HeaderColumnsBodyColumns
  2. 按照here所述实施访问者模式。

下面是按照上述建议可能实现的模式:

public class Main {

    interface Visitor {
        void visit(ReportHeader header);

        void visit(ReportBody body);
    }

    interface Visitable {
        void accept(Visitor visitor);
    }

    static class ReportHeader implements Visitable {

        private final List<String> columns = new ArrayList<>();
        private final List<String> extras = new ArrayList<>();

        @Override
        public void accept(Visitor visitor) { visitor.visit(this); }

        public List<String> getColumns() { return columns; }

        public List<String> getExtras() { return extras; }
    }

    static class ReportBody implements Visitable {

        private final List<String> columns = new ArrayList<>();

        @Override
        public void accept(Visitor visitor) { visitor.visit(this); }

        public List<String> getColumns() { return columns; }
    }

    static class ExcelReportVisitor implements Visitor {

        private final List<String> columns = new ArrayList<>();

        @Override
        public void visit(ReportHeader header) {
            columns.addAll(header.getColumns());
            columns.addAll(header.getExtras());
        }

        @Override
        public void visit(ReportBody body) { columns.addAll(body.getColumns()); }
    }

    static class PdfReportVisitor implements Visitor {

        private final List<String> columns = new ArrayList<>();

        @Override
        public void visit(ReportHeader header) {
            columns.addAll(header.getColumns());
            // no extras for PDF
        }

        @Override
        public void visit(ReportBody body) { columns.addAll(body.getColumns()); }
    }
}

您可以按如下方式使用它:

public static void main(String args[]) {
    ReportHeader header = new ReportHeader();
    ReportBody body = new ReportBody();

    List<Visitor> visitors = Arrays.asList(new PdfReportVisitor(), new ExcelReportVisitor());
    visitors.forEach(each -> {
        each.visit(header);
        each.visit(body);
    });

    // do something with the visitors like `visitor.exportReport()`
}

这种方法的优点:

  1. 每次您必须向 Visitor 添加新的报告部分时,都会在所有访问者实现中生成编译错误。这避免了人们忘记向 ifswitch 语句添加分支的典型编程错误。
  2. 如何构建报表的条件逻辑放在报表的实际实现中。不再需要条件。

这种方法的缺点:

  1. 考虑到您需要创建的额外抽象,肯定会很复杂。
  2. 有时 encapsulate/group 您的 Column 实例可能没有意义或不可能成为语义内聚的对象。例如,您可能以 ReportHeader 中的怪异结尾,如下所示:
static class ReportHeader implements Visitable {

    private final List<String> columns = new ArrayList<>();
    private final List<String> extras = new ArrayList<>();
    private final List<String> dataThatIsOnlyUsedByExcelReport = new ArrayList<>();

    @Override
    public void accept(Visitor visitor) { visitor.visit(this); }

    public List<String> getColumns() { return columns; }

    public List<String> getExtras() { return extras; }

    public List<String> getDataThatIsOnlyUsedByExcelReport() { return dataThatIsOnlyUsedByExcelReport; }
}