Java 使用自定义转换器映射对象类型
我正在尝试设计一个映射器,将一种对象类型转换为另一种,如下图所示: 该对象的结构如下(它不是JSON): 我的第一个解决方案是为每种类型使用mapper,但这需要大量代码重复来映射公共属性(即名称、类型、contentId)Java 使用自定义转换器映射对象类型,java,inheritance,mapping,Java,Inheritance,Mapping,我正在尝试设计一个映射器,将一种对象类型转换为另一种,如下图所示: 该对象的结构如下(它不是JSON): 我的第一个解决方案是为每种类型使用mapper,但这需要大量代码重复来映射公共属性(即名称、类型、contentId) documentsMapperFactory返回特定的映射器,该映射器将只映射与类型相关的属性并返回该对象实例 然而,在继承中还有更多的层次,我只有一个类型值,可以解析为具体的映射器。所以我必须在每个组件映射器中重复组件的特定字段映射。我在想,因为我知道层次结构,我可以创
documentsMapperFactory
返回特定的映射器,该映射器将只映射与类型相关的属性并返回该对象实例
然而,在继承中还有更多的层次,我只有一个类型值,可以解析为具体的映射器。所以我必须在每个组件映射器中重复组件的特定字段映射。我在想,因为我知道层次结构,我可以创建一些映射器,从上到下映射文档,即首先创建DownloadAppComponent,然后使用Component扩展它,然后使用BaseDocument特定属性扩展它。然而,除了在映射器中使用抽象类和继承之外,我还没有找到任何好的解决方案
有人能告诉我这是否是一个好的方法,或者我的案例是否存在任何问题或其他更好的解决方案吗
谢谢。至少有三种选择。第一种是更通用的,将
CmsDocument
的概念与BaseDocument
完全分离
....
private void setCommonValues(BaseDocument doc, CmsDocument cmsDocument) {
doc.setType(cmsDocument.getType());
doc.setName(cmsDocument.getName());
doc.setContentId(cmsDocument.getText(CONTENT_ID_PATH));
}
public DownloadAppComponent map(CmsDocument cmsDocument) {
DownloadAppComponent downloadAppComponent = new DownloadAppComponent();
// Call setCommonValues
setCommonValues(downloadAppComponent, cmsDocument);
downloadAppComponent.setIosURL(cmsDocument.getText(IOS_URL_PATH));
downloadAppComponent.setAndroidURL(cmsDocument.getText(ANDROID_URL_PATH));
downloadAppComponent.setHidden(Boolean.parseBoolean(cmsDocument.getText(HIDE_PATH)));
downloadAppComponent.setPromoText(cmsDocument.getText(DOWNLOAD_PROMO_TEXT_PATH));
return downloadAppComponent;
}
另外两个选项链接BaseDocument
和CmsDocument
类,因此它是一个包含选项的设计选择
第一选项
您可以创建一个方法,根据两个对象都来自BaseDocument
的事实来设置公共值
....
private void setCommonValues(BaseDocument doc, CmsDocument cmsDocument) {
doc.setType(cmsDocument.getType());
doc.setName(cmsDocument.getName());
doc.setContentId(cmsDocument.getText(CONTENT_ID_PATH));
}
public DownloadAppComponent map(CmsDocument cmsDocument) {
DownloadAppComponent downloadAppComponent = new DownloadAppComponent();
// Call setCommonValues
setCommonValues(downloadAppComponent, cmsDocument);
downloadAppComponent.setIosURL(cmsDocument.getText(IOS_URL_PATH));
downloadAppComponent.setAndroidURL(cmsDocument.getText(ANDROID_URL_PATH));
downloadAppComponent.setHidden(Boolean.parseBoolean(cmsDocument.getText(HIDE_PATH)));
downloadAppComponent.setPromoText(cmsDocument.getText(DOWNLOAD_PROMO_TEXT_PATH));
return downloadAppComponent;
}
对于另一个函数也是如此
public BaseDocument map(CmsDocument cmsDocument) {
BaseDocument document = documentsMapperFactory.getMapper(cmsDocument.getType()).map(cmsDocument);
// Call setCommonValues to remove duplication of code
setCommonValues(document, cmsDocument);
return document;
}
public BaseDocument map(CmsDocument cmsDocument) {
BaseDocument document = documentsMapperFactory.getMapper(cmsDocument.getType()).map(cmsDocument);
// Call init
document.init(cmsDocument);
return document;
}
第二选项 在
BaseDocument
类中创建方法init
private void init(CmsDocument cmsDocument) {
this.setType(cmsDocument.getType());
this.setName(cmsDocument.getName());
this.setContentId(cmsDocument.getText(CONTENT_ID_PATH));
}
在地图的主体部分
public DownloadAppComponent map(CmsDocument cmsDocument) {
DownloadAppComponent downloadAppComponent = new DownloadAppComponent();
// Call init
downloadAppComponent.init(cmsDocument);
downloadAppComponent.setIosURL(cmsDocument.getText(IOS_URL_PATH));
downloadAppComponent.setAndroidURL(cmsDocument.getText(ANDROID_URL_PATH));
downloadAppComponent.setHidden(Boolean.parseBoolean(cmsDocument.getText(HIDE_PATH)));
downloadAppComponent.setPromoText(cmsDocument.getText(DOWNLOAD_PROMO_TEXT_PATH));
return downloadAppComponent;
}
对于另一个函数也是如此
public BaseDocument map(CmsDocument cmsDocument) {
BaseDocument document = documentsMapperFactory.getMapper(cmsDocument.getType()).map(cmsDocument);
// Call setCommonValues to remove duplication of code
setCommonValues(document, cmsDocument);
return document;
}
public BaseDocument map(CmsDocument cmsDocument) {
BaseDocument document = documentsMapperFactory.getMapper(cmsDocument.getType()).map(cmsDocument);
// Call init
document.init(cmsDocument);
return document;
}
第三种选择 在
BaseDocument
上创建构造函数,将CmsDocument
作为参数
public BaseDocument(CmsDocument cmsDocument) {
this.setType(cmsDocument.getType());
this.setName(cmsDocument.getName());
this.setContentId(cmsDocument.getText(CONTENT_ID_PATH));
}
下载AppComponent中的
public DownloadAppComponent(CmsDocument cmsDocument) {
super(cmsDocument);
this.setIosURL(cmsDocument.getText(IOS_URL_PATH));
this.setAndroidURL(cmsDocument.getText(ANDROID_URL_PATH));
this.setHidden(Boolean.parseBoolean(cmsDocument.getText(HIDE_PATH)));
this.setPromoText(cmsDocument.getText(DOWNLOAD_PROMO_TEXT_PATH));
}
在这种情况下,您不需要map方法,您可以直接构建使用参数调用构造函数的对象
如果需要一个类,其方法
map
可以返回两个不同的实例,则可以将所需的类型作为参数传递:
public class Mapper {
private void setCommonValues(BaseDocument doc, CmsDocument cmsDocument) {
doc.setType(cmsDocument.getType());
doc.setName(cmsDocument.getName());
doc.setContentId(cmsDocument.getText(CONTENT_ID_PATH));
}
public BaseDocument map(CmsDocument cmsDocument, Class<? extends BaseDocument> clazz) {
BaseDocument doc = null;
if (clazz.getCanonicalName().equals(DownloadAppComponent.class.getCanonicalName()) {
DownloadAppComponent appComponent = new DownloadAppComponent();
doc = appComponent;
appComponent.setIosURL(cmsDocument.getText(IOS_URL_PATH));
appComponent.setAndroidURL(cmsDocument.getText(ANDROID_URL_PATH));
appComponent.setHidden(Boolean.parseBoolean(cmsDocument.getText(HIDE_PATH)));
appComponent.setPromoText(cmsDocument.getText(DOWNLOAD_PROMO_TEXT_PATH));
} else {
doc = new BaseDocument();
}
setCommonValues(doc);
return doc;
}
}
至少有三种选择。第一种是更通用的,将
CmsDocument
的概念与BaseDocument
完全分离
....
private void setCommonValues(BaseDocument doc, CmsDocument cmsDocument) {
doc.setType(cmsDocument.getType());
doc.setName(cmsDocument.getName());
doc.setContentId(cmsDocument.getText(CONTENT_ID_PATH));
}
public DownloadAppComponent map(CmsDocument cmsDocument) {
DownloadAppComponent downloadAppComponent = new DownloadAppComponent();
// Call setCommonValues
setCommonValues(downloadAppComponent, cmsDocument);
downloadAppComponent.setIosURL(cmsDocument.getText(IOS_URL_PATH));
downloadAppComponent.setAndroidURL(cmsDocument.getText(ANDROID_URL_PATH));
downloadAppComponent.setHidden(Boolean.parseBoolean(cmsDocument.getText(HIDE_PATH)));
downloadAppComponent.setPromoText(cmsDocument.getText(DOWNLOAD_PROMO_TEXT_PATH));
return downloadAppComponent;
}
另外两个选项链接BaseDocument
和CmsDocument
类,因此它是一个包含选项的设计选择
第一选项
您可以创建一个方法,根据两个对象都来自BaseDocument
的事实来设置公共值
....
private void setCommonValues(BaseDocument doc, CmsDocument cmsDocument) {
doc.setType(cmsDocument.getType());
doc.setName(cmsDocument.getName());
doc.setContentId(cmsDocument.getText(CONTENT_ID_PATH));
}
public DownloadAppComponent map(CmsDocument cmsDocument) {
DownloadAppComponent downloadAppComponent = new DownloadAppComponent();
// Call setCommonValues
setCommonValues(downloadAppComponent, cmsDocument);
downloadAppComponent.setIosURL(cmsDocument.getText(IOS_URL_PATH));
downloadAppComponent.setAndroidURL(cmsDocument.getText(ANDROID_URL_PATH));
downloadAppComponent.setHidden(Boolean.parseBoolean(cmsDocument.getText(HIDE_PATH)));
downloadAppComponent.setPromoText(cmsDocument.getText(DOWNLOAD_PROMO_TEXT_PATH));
return downloadAppComponent;
}
对于另一个函数也是如此
public BaseDocument map(CmsDocument cmsDocument) {
BaseDocument document = documentsMapperFactory.getMapper(cmsDocument.getType()).map(cmsDocument);
// Call setCommonValues to remove duplication of code
setCommonValues(document, cmsDocument);
return document;
}
public BaseDocument map(CmsDocument cmsDocument) {
BaseDocument document = documentsMapperFactory.getMapper(cmsDocument.getType()).map(cmsDocument);
// Call init
document.init(cmsDocument);
return document;
}
第二选项 在
BaseDocument
类中创建方法init
private void init(CmsDocument cmsDocument) {
this.setType(cmsDocument.getType());
this.setName(cmsDocument.getName());
this.setContentId(cmsDocument.getText(CONTENT_ID_PATH));
}
在地图的主体部分
public DownloadAppComponent map(CmsDocument cmsDocument) {
DownloadAppComponent downloadAppComponent = new DownloadAppComponent();
// Call init
downloadAppComponent.init(cmsDocument);
downloadAppComponent.setIosURL(cmsDocument.getText(IOS_URL_PATH));
downloadAppComponent.setAndroidURL(cmsDocument.getText(ANDROID_URL_PATH));
downloadAppComponent.setHidden(Boolean.parseBoolean(cmsDocument.getText(HIDE_PATH)));
downloadAppComponent.setPromoText(cmsDocument.getText(DOWNLOAD_PROMO_TEXT_PATH));
return downloadAppComponent;
}
对于另一个函数也是如此
public BaseDocument map(CmsDocument cmsDocument) {
BaseDocument document = documentsMapperFactory.getMapper(cmsDocument.getType()).map(cmsDocument);
// Call setCommonValues to remove duplication of code
setCommonValues(document, cmsDocument);
return document;
}
public BaseDocument map(CmsDocument cmsDocument) {
BaseDocument document = documentsMapperFactory.getMapper(cmsDocument.getType()).map(cmsDocument);
// Call init
document.init(cmsDocument);
return document;
}
第三种选择 在
BaseDocument
上创建构造函数,将CmsDocument
作为参数
public BaseDocument(CmsDocument cmsDocument) {
this.setType(cmsDocument.getType());
this.setName(cmsDocument.getName());
this.setContentId(cmsDocument.getText(CONTENT_ID_PATH));
}
下载AppComponent中的
public DownloadAppComponent(CmsDocument cmsDocument) {
super(cmsDocument);
this.setIosURL(cmsDocument.getText(IOS_URL_PATH));
this.setAndroidURL(cmsDocument.getText(ANDROID_URL_PATH));
this.setHidden(Boolean.parseBoolean(cmsDocument.getText(HIDE_PATH)));
this.setPromoText(cmsDocument.getText(DOWNLOAD_PROMO_TEXT_PATH));
}
在这种情况下,您不需要map方法,您可以直接构建使用参数调用构造函数的对象
如果需要一个类,其方法
map
可以返回两个不同的实例,则可以将所需的类型作为参数传递:
public class Mapper {
private void setCommonValues(BaseDocument doc, CmsDocument cmsDocument) {
doc.setType(cmsDocument.getType());
doc.setName(cmsDocument.getName());
doc.setContentId(cmsDocument.getText(CONTENT_ID_PATH));
}
public BaseDocument map(CmsDocument cmsDocument, Class<? extends BaseDocument> clazz) {
BaseDocument doc = null;
if (clazz.getCanonicalName().equals(DownloadAppComponent.class.getCanonicalName()) {
DownloadAppComponent appComponent = new DownloadAppComponent();
doc = appComponent;
appComponent.setIosURL(cmsDocument.getText(IOS_URL_PATH));
appComponent.setAndroidURL(cmsDocument.getText(ANDROID_URL_PATH));
appComponent.setHidden(Boolean.parseBoolean(cmsDocument.getText(HIDE_PATH)));
appComponent.setPromoText(cmsDocument.getText(DOWNLOAD_PROMO_TEXT_PATH));
} else {
doc = new BaseDocument();
}
setCommonValues(doc);
return doc;
}
}
我必须说,我更喜欢第一种方法,因为正如你所说的,它不会使这两个类相互依赖,它通过保持SoC和单一责任原则帮助代码更干净。我仍然不明白应该把
setCommonValues()
方法放在哪里。它们不能都在同一个类中,因为它不会编译。map()
方法将是不明确的。必须像调用super.setCommonValues()
那样调用helper方法,但我必须为映射器类添加另一个继承级别(即DownloadAppComponentMapper extends ComponentMapper
,以及ComponentMapper extends BaseDocumentMapper
等)。我能以某种方式避免吗?@MariuszMiesiak如果必须将方法map
添加到同一类中,则可以重命名它们。但您也可以为它们创建两个类,而无需重命名。如果您选择一个具有两个重命名的map
方法的类,则可以将方法setCommonValues
添加到该类中。否则,最好的解决方案是使用具体方法setCommonValue
和抽象方法map
创建一个抽象类,如果只需要一个类和一个方法传递一个带有要创建的类类型的参数,则为map
@MariuszMiesiak的每个自定义实现创建两个子类。我在答案中添加了这个解决方案。我必须说我更喜欢第一种方法,因为正如你所说的,它不会使这两个类相互依赖,它通过保持SoC和单一责任原则帮助代码变得更干净。我仍然不明白应该把setCommonValues()
方法放在哪里。它们不能都在同一个类中,因为它不会编译。map()
方法将是不明确的。必须像调用super.setCommonValues()
那样调用helper方法,但我必须为映射器类添加另一个继承级别(即DownloadAppComponentMapper extends ComponentMapper
,以及ComponentMapper extends BaseDocumentMapper
等)。我能以某种方式避免吗?@MariuszMiesiak如果必须将方法map
添加到同一类中,则可以重命名它们。但您也可以为它们创建两个类,而无需重命名。如果您选择一个具有两个重命名的map
方法的类,则可以将方法setCommonValu添加到该类中