【问题标题】:Mapping model classes with Kotlin使用 Kotlin 映射模型类
【发布时间】:2018-07-20 22:31:14
【问题描述】:

我正在寻找一种方法来改进我的应用中不同层的模型类之间的映射机制。

我在业务逻辑层(例如 Presenter)中有模型类,即我在应用程序中使用的模型类。例如一个用户类。

我有服务器端实体,我使用这些实体通过 Retrofit 或任何其他技术反序列化来自服务器端端点的响应。这些通常包含字段的 Gson 注释。例如 ApiUserResponse 类。

我有 DTO 实体,这些实体代表我的应用程序中的数据库表。我现在正在使用 Realm,但我使用过 ORMlite 和 Room。这些类型的类还包含与 DB 相关的注释。例如 UserDTO 类。

我的缓存存储库(从数据库获取数据的存储库)和我的网络存储库(从服务器端获取数据的存储库)都公开了 RxJava 方法,返回包装在 Observable 类中的响应。

我现在解析 DTO 实体和服务器端实体的工作是将扩展函数添加到 Observable 类,该类接收函数作为参数。这就是我用来将 DTO 和服务器端实体解析为模型类的机制。例如:

myLoginRepository.getUser("someId")
    .mapTo(::myMappingFunction)

...

fun myMappingFunction(userResponse: ApiUserResponse): User {
    return User(userResponse.id, userResponse.name, userResponse.lastname)
}

在内部,mapTo 扩展函数所做的唯一事情是使用 flatMap 来扁平化 Observable 流并使用映射来解析集合。

我认为这是实现模型相关类之间映射的一种很好的方法,但我正在寻找一种改进方法或做一些完全不同但需要更少编码的方法。显然这个例子非常简单,但目前我有一个 Mappings.kt 文件,随着每个新的映射函数,它都在疯狂增长。

有什么建议吗?

【问题讨论】:

  • 你应该在每一层都有映射类文件,只涉及同一层的模型类。
  • 请问,你能分享你的 Observable 扩展 mapTo() 吗?谢谢!

标签: android kotlin mapping kotlin-extension


【解决方案1】:

也许http://modelmapper.org/ 适合你。这样您就可以节省自己的映射函数,甚至可以指定一些复杂的映射函数。但是,它可能会在性能方面付出一些代价(看起来像是一路反射访问?)。

注意:我自己还没有使用过这个库,但它看起来很有希望推荐它。在我帮助的最后一个项目中,他们构建了自己的模型映射器,这可能不是一个好主意(很多极端情况被遗忘,然后导致后来出现奇怪的行为或手动映射错误的字段;但是您的变体可能会受到也是)。

刚刚发现另一篇关于映射框架及其性能的文章: http://www.baeldung.com/java-performance-mapping-frameworks 看起来对 ModelMapper 不太好

您可能还只想生成映射函数。类似于你现在所拥有的。还是只使用实时模板?

手动编写映射迟早会引入您不再那么容易识别的错误,除非您相应地测试它们。你做什么,对吧? ;-)

【讨论】:

  • 是的,f *** 它,似乎除了拥有像我现在这样的映射之外别无他法。是的,我有 UT :)
  • 如果你想保留你的 DTO 并且你不想使用反射或任何提到的框架,那么不......没有简单的方法;-) 如果反射对你来说没问题,那么可能有一种“易于使用”(不一定易于编写)的方法;-)
  • 不,就像你说的,如果你开始使用这个基于反射的库,那么你就有可能出现错误,而且性能会下降。我更愿意坚持我现在正在做的事情。
  • 根据您的用途,这可能是一种可接受的方法。有时坚持使用库比编写大量映射函数更方便。但是:只要您坚持使用任何模型映射实用程序或映射功能,您就可能不再质疑您的 DTO(数量/有用性)。不要过早地衡量性能。您已经知道一种可能更高效的方法,但是您的 Mapping.kt 会变得臃肿。您甚至可以稍后生成一个高性能的。我会从最方便的方式开始,只在必要时切换
【解决方案2】:

您可以使用转换器将 DTO 对象转换为实体,反之亦然。

Converter

ConversionService

你可以实现Converter接口,它是一个功能接口,你必须实现convert方法,然后将你的DTO转换为实体,你要转换它们的地方,你应该注入ConversionService并使用convert方法。

【讨论】:

  • Spring 框架和转换器有什么关系?
【解决方案3】:

你应该只有一个类代表网络中的实体和模型类

如果你想使用 Room 数据库和 Retrofit 进行 Api 调用,你应该像这样使用

@Entity("tableName = something")
data class Something{

    @SerializedName("name")
    val name : String,
    @SerializedName("number")
    val number : Int}

【讨论】:

  • 如果服务器端响应与数据库模型完全不同怎么办。我无法根据服务器端的响应建立数据库关系。如果我的 UserDTO 中有一个 schedules 字段,但时间表是从一个完全不同的端点提取的,该怎么办。这是一个简单的案例。我想以适当的方式映射我的数据库关系。而且我不想让我的数据库映射基于服务器端响应(可能会改变)
  • 想象一下,我有一个 ImageGallery 集合,其中包含一个 Tag 类的集合,并且我在 ImageGallery 中也有一个 Image 集合,每个 Image 类还包含一个 Tag 集合。拥有这样的模型的数据库映射和服务器端注释实际上是不可能的,因为您需要拥有像 ImageGalleryTag 和 ImageTag 这样的多对多中间类。
  • 为此,您应该使用映射器将所需内容保存在数据库中:)
猜你喜欢
  • 2021-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多