如何使服务方法处理子类的特殊性

How to make a service method handle subclasses specificities

我有一个应用程序必须处理多种类型的文档。

我有一个必须生成报告的服务 generateReport(DocumentEntity),将为 DocumentEntity 的每种类型的子项调用此服务。

问题是我们要构建的报告取决于文档的类型。

我的下面的代码有效,但我觉得有更好的方法:

假设我们有扩展 DocumentEntity 的 DocumentDonationEntity 和 DocumentBookEntity:

public abstract class DocumentEntity {
    protected Long id;
    protected String name;
    // getters & setters
}

public class DocumentDonationEntity extends DocumentEntity {
    private String donatorName;
    // getters & setters
}

public class DocumentBookEntity extends DocumentEntity {
    private Long price;
    // getters & setters
}

以及扩展 ReportData 的 ReportDataBook 和 ReportDataDonation:

public abstract class ReportData {
    protected Long id;
    protected String name;

    public void checkNotNull() throws MyException {
        if this.id == null
            throw new myException();
        if this.name == null
            throw new myException();
        // other generic stuff
    }
    // getters & setters
}

public class ReportDataBook extends ReportData {
    private String price;

    public void checkNotNull() throws MyException {
        super.checkNotNull();
        if this.price == null
            throw new myException();
        // other stuff specific to books
    }
    // getters & setters
}

public class ReportDataDonation extends ReportData {
    private String donatorName;

    public void checkNotNull() throws MyException {
        super.checkNotNull();
        if this.donatorName == null
            throw new MyException();
        // other stuff specific to donations
    }
    // getters & setters
}

加上一个实用程序 class :

public class Utils {
    public ReportData fillReport(DocumentEntity document) throws Exception {
        if (document instanceof DocumentBookEntity) {
            ReportDataBook data = new ReportDataBook();
            completeReportData(data, (DocumentBookEntity) document);
            completeReportData(data, document);
            return data;
        } else if (document instanceof DocumentDonationEntity) {
            ReportDataDonation data = new ReportDataDonation ();
            completeReportData(data, (DocumentDonationEntity) document);
            completeReportData(data, document);
            return data;
        }
        return null;
    }

    public void completeReportData(ReportData data, DocumentEntity document) {
        // some generic stuff
    }

    public void completeReportData(ReportDataBook data, DocumentBookEntity document) {
        // some stuff specific to books
    }

     public void completeReportData(ReportDataDonation data, DocumentDonationEntity document) {
        // some stuff specific to donations
    }

}

那我有一个服务:

@Service
public class MyService implements IMyService {

    @Override
    public boolean generateReport(DocumentEntity document) throws MyException {
        ReportData data = initializeReport(document);
        // do some other stuff
    }

    private ReportData initializeReport(DocumentEntity document) throws myException {
        if (document instanceof DocumentBookEntity) {
            ReportDataBook data = (ReportDataBook) utils.fillReport(document);
            data.checkNotNull();
            return data;
        } else if (document instanceof DocumentDonationEntity) {
            ReportDataDonation data = (ReportDataBook) utils.fillReport(document);
            data.checkNotNull();
            return data;
        }
        return null;
    }
}

每当您发现 instanceof 时,您都可以考虑根据 http://refactoring.com/catalog/replaceConditionalWithPolymorphism.html

重构为多态性

我做了一些 UML 图,因为我发现这样更容易理解具有子class 层次结构的设计:

我会考虑 Factory Method 模式:

我将您的 DocumentEntity 层次结构视为此模式中的 Creator,而 ReportDataProduct

上面的例子只展示了一个工厂方法的实现。另一个 (DocumentBookEntity) 会 return 一个 new ReportBookDonation().

ReportData classes 然后处理 DocumentEntity 子类型的所有细节。我不认为你真的需要 MyService 东西了。 MyService.initializeReport() 应移至 ReportData 层级;它现在在 subclasses 中是多态的(消除了 instanceof 条件)。它与 Utils.fillReport() 的代码相同 - 它是由特定版本的报告提供的服务。

如果其他 Utils 方法不依赖于子类型,则将它们放在 ReportData 抽象 class 中。就个人而言,我将其称为 Report class,因为对象更多地是关于行为(服务)而不是数据结构。

因此,要生成报告,它看起来像这样:

DocumentEntity doc;   // it's really an instance of a subclass
...
ReportData reporter = doc.createReportService();   // factory method call (polymorphic)
...
reporter.initializeReport();  // lazy initialization

您可以将 initializefill 的东西放在子 class 的构造函数中,一旦您调用工厂方法,它就会生成报告。