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

Java 使用自定义转换器映射对象类型,java,inheritance,mapping,Java,Inheritance,Mapping,我正在尝试设计一个映射器,将一种对象类型转换为另一种,如下图所示: 该对象的结构如下(它不是JSON): 我的第一个解决方案是为每种类型使用mapper,但这需要大量代码重复来映射公共属性(即名称、类型、contentId) documentsMapperFactory返回特定的映射器,该映射器将只映射与类型相关的属性并返回该对象实例 然而,在继承中还有更多的层次,我只有一个类型值,可以解析为具体的映射器。所以我必须在每个组件映射器中重复组件的特定字段映射。我在想,因为我知道层次结构,我可以创

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

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

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

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添加到该类中