Java void函数编程,紧密耦合和样板

问题描述

当目标对象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",41));

    //shared columns
    Column test4 = new Column("test4",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. 有时,将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; }
}