如何使服务方法处理子类的特殊性
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
,而 ReportData
是 Product
:
上面的例子只展示了一个工厂方法的实现。另一个 (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
您可以将 initialize
和 fill
的东西放在子 class 的构造函数中,一旦您调用工厂方法,它就会生成报告。
我有一个应用程序必须处理多种类型的文档。
我有一个必须生成报告的服务 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
,而 ReportData
是 Product
:
上面的例子只展示了一个工厂方法的实现。另一个 (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
您可以将 initialize
和 fill
的东西放在子 class 的构造函数中,一旦您调用工厂方法,它就会生成报告。