使用自定义转换器映射对象类型

时间:2016-09-27 15:10:47

标签: java inheritance mapping

我正在尝试设计一个将一种对象类型转换为另一种对象类型的映射器,如下图所示:

enter image description here

对象的结构如下(它不是JSON):

{
"type": "DownloadAppComponent",
"name": "Download App",
"contentId": "download-app",
"properties": {
    "iosUrl": "http://apple.com",
    "androidUrl": "http: //google.com",
    "promoText": "Download our app",
    "hidden": false
}

我的第一个解决方案是为每种类型设置映射器,但这需要大量的代码重复来映射公共属性(即名称,类型,contentId)。

public DownloadAppComponent map(CmsDocument cmsDocument) {
    DownloadAppComponent downloadAppComponent = new DownloadAppComponent();

    downloadAppComponent.setType(cmsDocument.getType()); // <-- this will be duplicated in each mapper
    downloadAppComponent.setName(cmsDocument.getName()); // <-- this will be duplicated in each mapper
    downloadAppComponent.setContentId(cmsDocument.getText(CONTENT_ID_PATH)); // <-- this will be duplicated in each mapper
    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;
}

我一直在尝试重构该代码,并提出了通用的BaseDocumentMapper:

public BaseDocument map(CmsDocument cmsDocument) {
    BaseDocument document = documentsMapperFactory.getMapper(cmsDocument.getType()).map(cmsDocument);
    document.setType(cmsDocument.getType());
    document.setName(cmsDocument.getName());
    document.setContentId(cmsDocument.getText(CONTENT_ID_PATH));
    return document;
}

documentsMapperFactory返回特定的映射器,它只映射与类型相关的属性并返回该对象实例。

但是继承中还有更多级别,我只有一个类型值,我可以将其解析为具体映射器。所以我必须在每个组件映射器中重复Component的特定字段映射。我在想,因为我知道层次结构,我可以创建一些映射器,将文件从Up映射到Bottom,即创建第一个DownloadAppComponent,然后使用Component扩展它,然后使用BaseDocument特定属性。但是,除了在映射器中使用抽象类和继承之外,我还没有找到任何好的解决方案。

如果这是一个好的方法或者我的案例是否有任何问题或其他更好的解决方案,有人可以建议我吗?

谢谢。

1 个答案:

答案 0 :(得分:2)

至少有三种选择。第一个是更通用的,将CmsDocument的概念与BaseDocument完全分开。

其他两个选项链接BaseDocumentCmsDocument类,因此它是一个选项选择的设计选择。

第一个选项 您可以创建一个方法来根据两个对象派生自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;
}

第二个选项

init

中创建方法BaseDocument
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 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));
}

在DownloadAppComponent

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

您可以将其调用为:

Mapper mapper = new Mapper();
CmsDocument cmsDocument = ...

BaseDocument doc = mapper.map(cmsDocument, BaseDocument.class);

DownloadAppComponent downloadAppComponent = (DownloadAppComponent) mapper.map(cmsDocument, DownloadAppComponent.class);