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

时间:2015-09-17 15:00:55

标签: inheritance design-patterns methods service subclass

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

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

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

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

假设我们有DocumentDonationEntity&扩展DocumentEntity的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
}

和ReportDataBook&和ReportDataDonation扩展了ReportData:

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
}

加上一个utils类:

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;
    }
}

1 个答案:

答案 0 :(得分:0)

每当您找到instanceof时,您可以考虑根据http://refactoring.com/catalog/replaceConditionalWithPolymorphism.html重构多态性

我做了一些UML图表,因为我觉得用子类层次结构来设置一个设计更容易:

UML of the class hierarchies

我会考虑Factory Method模式:

Factory Method GoF pattern in UML

我将DocumentEntity层次结构视为此模式中的Creator,将ReportData视为Product

Applying Factory Method to example

上面的示例仅显示了一个工厂方法实现。另一个(DocumentBookEntity)将返回new ReportBookDonation()

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

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

因此,要生成报告,它将如下所示:

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

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