Android项目数据模型分类详解

在 Android 分层架构中,数据模型根据其职责和使用层级分为 5 种核心类型,每种都有明确的定位和转换规则:


1. DTO (Data Transfer Object) - 数据传输对象

  • 定位数据层(网络通信子层)

  • 核心职责

    • 映射 API 返回的原始 JSON/XML 结构
    • 处理网络协议特有字段(如状态码、错误消息)
  • 特点

    • 字段命名与 API 完全一致
    • 包含网络通信专用注解(如 Retrofit 的 @SerializedName
    • 无业务逻辑,纯数据结构
  • 示例

    less 复制代码
    data class UserDTO(
        @SerializedName("user_id") 
        val id: String,
        
        @SerializedName("full_name")
        val name: String,
        
        @SerializedName("created_at")
        val createTime: String  // 原始时间字符串
    )
  • 转换方向:DTO → Domain Model


2. PO (Persistent Object) / Entity - 持久化对象

  • 定位数据层(本地存储子层)

  • 核心职责

    • 映射数据库表结构(Room/SQLite)
    • 定义本地存储方案(如字段类型、主键、索引)
  • 特点

    • 包含数据库框架注解(如 Room 的 @Entity, @PrimaryKey
    • 数据类型适配存储引擎(如 Date 存为 Long)
    • 可能包含数据库优化字段(如版本号、时间戳)
  • 示例

    less 复制代码
    @Entity(tableName = "users")
    data class UserPO(
        @PrimaryKey
        val id: String,
        
        @ColumnInfo(name = "display_name")
        val name: String,
        
        @ColumnInfo(name = "last_update_time")
        val updateTime: Long  // 时间戳格式
    )
  • 转换方向:PO ↔ Domain Model


3. Domain Model - 领域模型

  • 定位领域层(架构核心)

  • 核心职责

    • 表达业务核心概念和规则
    • 封装业务校验逻辑
    • 作为各层数据流转的中枢
  • 特点

    • 纯 Kotlin/Java 对象(无任何框架依赖)
    • 包含业务方法(如 isVIP()
    • 字段命名符合业务术语
    • 不可变(推荐使用 val
  • 示例

    kotlin 复制代码
    data class User(
        val id: String,
        val name: String,
        val joinDate: LocalDate,
        val creditPoints: Int
    ) {
        // 业务逻辑方法
        fun isVIP() = creditPoints > 1000
    }
  • 转换关系

    • ← 来自 DTO/PO
    • → 流向 VO

4. VO (View Object) - 视图对象

  • 定位UI 层

  • 核心职责

    • 为界面展示优化的数据结构
    • 包含 UI 特有的字段和格式
    • 适配视图组件需求
  • 特点

    • 包含展示专用字段(如资源 ID、格式化文本)
    • 扁平化结构便于 UI 直接使用
    • 可能组合多个 Domain Model
  • 示例: data class UserVO( val name: String, val avatarResId: Int, // 本地资源ID val joinInfo: String, // 格式化文本:"加入3年" val isVip: Boolean, val vipBadgeColor: Int // VIP专属颜色 )

  • 转换位置:ViewModel 中转换 Domain → VO


5. Request Model - 请求对象(特殊场景)

  • 定位数据层(网络请求子层)

  • 核心职责

    • 封装向 API 发送的请求体
    • 处理请求特殊要求(如加密字段)
  • 特点

    • 结构对应 API 要求的请求格式
    • 可能包含鉴权 token 等临时字段
  • 示例

    kotlin 复制代码
    data class LoginRequest(
        val username: String,
        val password: String,
        val device_id: String,
        val sign: String // 加密签名
    )

模型转换关系图

关键设计原则

  1. 单一职责原则

    每类模型只处理单一层次的问题(如 DTO 只处理 JSON 映射)

  2. 隔离变化

    • API 字段变更只需修改 DTO 转换逻辑
    • UI 改版只需调整 VO 结构
  3. 安全边界

    敏感字段(如密码)仅在 Request 中出现,不会泄漏到 VO

  4. 性能优化

    • VO 预计算展示数据减少 UI 线程负担
    • PO 包含数据库优化字段

为何需要多层模型?实际案例

假设用户个人页需要展示:

css 复制代码
[头像] 张三(VIP徽章)
加入时间:2021年(3年前)

数据流转过程

  1. 网络层 收到 DTO:

    json 复制代码
    {
      "user_id": "123",
      "avatar_url": "https://...",
      "full_name": "张三",
      "create_time": "2021-03-12T00:00:00Z",
      "point_balance": 1500
    }
  2. 数据层 转换为 Domain Model:

    ini 复制代码
    User(
      id = "123",
      name = "张三",
      joinDate = LocalDate.of(2021, 3, 12), // 转换为日期对象
      creditPoints = 1500
    )
  3. ViewModel 转换为 VO:

    ini 复制代码
    UserVO(
      name = "张三",
      avatarResId = R.drawable.vip_avatar, // 根据URL预加载
      joinInfo = "加入3年",                // 计算时间差
      isVip = true,
      vipBadgeColor = Color.RED
    )

常见问题解决方案

问题1:模型转换代码冗余?

👉 方案:使用扩展函数集中管理

kotlin 复制代码
// DTO转Domain
fun UserDTO.toDomain() = User(
    id = user_id,
    name = full_name,
    joinDate = parseDate(created_at)
)

// Domain转VO
fun User.toVO(resProvider: ResourceProvider) = UserVO(
    name = name,
    avatarResId = resProvider.loadAvatar(id),
    joinInfo = "加入${LocalDate.now().year - joinDate.year}年"
)

问题2:小型项目是否需要完整分层?

👉 精简方案:

复制代码
数据层:DTO 直接转换为 VO
但保留 Domain Model 预留扩展点

问题3:多数据源合并?

👉 在 Repository 中处理:

kotlin 复制代码
fun getUserData(): User {
    val remote = api.getUser().toDomain()
    val local = db.getUserPreferences()
    return remote.copy(settings = local) // 合并网络和本地数据
}

严格区分模型类型

相关推荐
cypking19 分钟前
electron中IPC 渲染进程与主进程通信方法解析
前端·javascript·electron
西陵26 分钟前
Nx带来极致的前端开发体验——借助playground开发提效
前端·javascript·架构
江城开朗的豌豆40 分钟前
Element UI动态组件样式修改小妙招,轻松拿捏!
前端·javascript·vue.js
float_六七1 小时前
JavaScript:现代Web开发的核心动力
开发语言·前端·javascript
zhaoyang03011 小时前
vue3笔记(2)自用
前端·javascript·笔记
德育处主任Pro2 小时前
# JsSIP 从入门到实战:构建你的第一个 Web 电话
前端
拾光拾趣录2 小时前
setTimeout(1) 和 setTimeout(2) 的区别
前端·v8
拾光拾趣录2 小时前
内存泄漏的“隐形杀手”
前端·性能优化
摸鱼仙人~2 小时前
HttpServletRequest深度解析:Java Web开发的核心组件
java·开发语言·前端
索西引擎2 小时前
【工程化】浅谈前端构建工具
前端·webpack·gulp·turbopack