12 KiB
12 KiB
代码重构前后对比
整体改进
原项目问题
❌ 每次调用都重新创建对象(浪费资源,状态丢失)
❌ 流程不清晰,难以扩展
❌ 职责混乱,不知道在哪里插入新功能
❌ 数据模型不完整(缺少中间状态的定义)
❌ 无法直观看出数据在各步骤间如何流动
改进后的特点
✅ 对象复用(一次初始化,重复使用)
✅ 清晰的4步数据流(每步职责明确)
✅ 易于扩展(预留位置清楚标记)
✅ 完整的数据模型(每步都有输出定义)
✅ 充分的注释和伪代码(易于理解和修改)
具体对比
对比1:主程序入口
原代码 (Main.kt)
fun main() {
} // 空的,什么都没有
新代码 (Main.kt)
fun main() {
// 1. 加载样本轨迹
val route = sampleRoute
// 2. 创建模拟器(只创建一次)
val simulator = TrajectorySimulator(route)
// 3. 配置参数
simulator.setPhysicsParams(...)
simulator.setNoiseParams(...)
// 4. 模拟循环(逐时间步调用)
for (time in 0.0..duration step 0.1) {
val motionState = simulator.simulate(time)
println(motionState)
}
}
改进:清晰展示了如何使用,每个步骤有注释说明
对比2:主处理流程
原代码 (Runner.getCoordinate)
class Runner(val route: List<MetaCoordinate>) {
fun getCoordinate(time: Double): Coordinate {
val path = PathLinearize(route) // ❌ 每次都创建
val physicEngine = RunnerPhysic() // ❌ 每次都创建
val noiseEngine = PathNoiseEngine() // ❌ 每次都创建
val distance = physicEngine.getDistance(time)
val coordinate = path.getPointDistance(distance)
val finalCoordinate = noiseEngine.applyNoise(coordinate.lat, coordinate.lon, physicEngine)
return finalCoordinate
}
}
问题:
- 每次都重新创建对象,浪费资源
- NoiseEngine 中的漂移状态会丢失
- 流程混乱,无法清晰看出数据如何流转
- 返回值类型混乱(Coordinate vs 其他)
新代码 (TrajectorySimulator.simulate)
class TrajectorySimulator(route: List<MetaCoordinate>) {
// 一次初始化,重复使用
private val pathInterpolator: PathInterpolator = PathInterpolator(route)
private val physicsEngine: RunnerPhysics = RunnerPhysics()
private val noiseProcessor: NoiseProcessor = NoiseProcessor()
fun simulate(time: Double): MotionState {
// ════════════════════════════════════════════
// 步骤1:计算物理状态
// ════════════════════════════════════════════
val physicsState = physicsEngine.calculate(time) // ✅ 返回 PhysicsState
// ════════════════════════════════════════════
// 步骤2:路径插值
// ════════════════════════════════════════════
val rawCoordinate = pathInterpolator.interpolate(physicsState.distance) // ✅ 返回 RawCoordinate
// ════════════════════════════════════════════
// 步骤3:添加噪声
// ════════════════════════════════════════════
val noisyCoordinate = noiseProcessor.applyNoise(rawCoordinate, physicsState) // ✅ 返回 NoisyCoordinate
// ════════════════════════════════════════════
// 步骤4:组装最终状态
// ════════════════════════════════════════════
return MotionState(...) // ✅ 返回完整的 MotionState
}
}
改进:
- 对象复用,提高效率
- 4步流程清晰,每步职责明确
- 每步都有明确的输入输出类型
- 易于理解和调试
对比3:坐标数据模型
原代码
// 只有两个坐标类,中间状态混乱
data class Coordinate(val lon: Double, val lat: Double, val time: Double)
data class MetaCoordinate(val lon: Double, val lat: Double)
新代码
// 完整的数据流定义
data class MetaCoordinate(val lon: Double, val lat: Double) // 原始轨迹点
data class RawCoordinate(val lon: Double, val lat: Double) // 无噪声插值点
data class NoisyCoordinate(
val lon: Double,
val lat: Double,
val gpsError: Double // 记录噪声大小
)
data class Coordinate(val lon: Double, val lat: Double, val time: Double) // 带时间的坐标
改进:
- 每个数据流阶段都有明确的数据类型
- 便于调试和理解数据的变换过程
- 预留了噪声信息的记录
对比4:物理引擎
原代码
class RunnerPhysic {
fun getDistance(time: Double): Double {} // ❌ 未实现,空的
}
新代码
class RunnerPhysics(
private val totalDistance: Double = 0.0,
private val duration: Double = 1000.0,
private val maxVelocity: Double = 4.0
) {
/**
* 计算指定时间的物理状态
*
* @param time 当前时间(秒)
* @return 包含距离、速度、加速度的物理状态
*/
fun calculate(time: Double): PhysicsState {
// 伪代码说明:
// 1. 计算距离
// 2. 计算速度
// 3. 计算加速度
val distance = getDistance(time)
val velocity = 0.0 // TODO: 实现
val acceleration = 0.0 // TODO: 实现
return PhysicsState(time, distance, velocity, acceleration)
}
fun getDistance(time: Double): Double {
// 伪代码说明
return 0.0 // TODO: 实现
}
}
改进:
- 返回类型改为 PhysicsState(包含完整的物理信息)
- 添加了伪代码说明实现思路
- 添加了TODO标记,清楚地标出需要实现的地方
- 参数化了初始值(便于配置)
对比5:路径插值器
原代码 (pathLinearlizer.kt)
class PathLinearize(val nodes: List<MetaCoordinate>) {
private val segments = nodes.windowed(2).map { (a, b) ->
val distance = haversine(a, b)
Triple(a, b, distance)
}
fun haversine(node1: MetaCoordinate, node2: MetaCoordinate): Double {
// ... 完整的Haversine算法(逻辑保留)
}
fun getPointDistance(totalDistance: Double): MetaCoordinate {
// ... 插值逻辑
}
}
新代码 (PathInterpolator.kt)
class PathInterpolator(val nodes: List<MetaCoordinate>) {
private val segments = nodes.windowed(2).map { (a, b) ->
val distance = calculateHaversineDistance(a, b) // ✅ 改名:更清晰
Triple(a, b, distance)
}
fun interpolate(totalDistance: Double): RawCoordinate { // ✅ 改名:更直观
// ... 同样的插值逻辑,完全保留
}
private fun calculateHaversineDistance(node1: MetaCoordinate, node2: MetaCoordinate): Double {
// ✅ Haversine逻辑完全保留,只改了方法名
// 和原代码一模一样
}
}
改进:
- 类名改为 PathInterpolator(更准确)
- 方法名改为
interpolate和calculateHaversineDistance(更清晰) - 返回类型改为 RawCoordinate(更明确)
- 核心逻辑完全保留,只改了命名
对比6:噪声处理
原代码
class PathNoiseEngine(
private val gpsErrorStdDev: Double = 0.00002,
private val driftWeight: Double = 0.3
) {
private val random = Random()
private var driftLat = 0.0
private var driftLon = 0.0
fun applyNoise(rawLat: Double, rawLon: Double, physic: RunnerPhysic): Coordinate {
// ... 白噪声和漂移逻辑(完全保留)
return Coordinate(rawLon+whiteNoiseLon+driftLon, rawLat+whiteNoiseLat+driftLat, time = Double)
// ❌ time = Double 是个错误,应该是实际时间值
}
}
新代码 (GpsNoiseEngine.kt)
class GpsNoiseEngine(
private val gpsErrorStdDev: Double = 0.00002,
private val driftWeight: Double = 0.3
) {
/**
* 应用GPS噪声到坐标
*
* 伪代码:
* 1. 生成白噪声
* 2. 生成漂移
* 3. 合并噪声
*/
fun applyNoise(
rawLon: Double,
rawLat: Double,
physicsState: PhysicsState
): Triple<Double, Double, Double> { // ✅ 更清晰的返回类型
// 第1步:白噪声(逻辑完全保留)
val whiteNoiseLat = random.nextGaussian() * gpsErrorStdDev
val whiteNoiseLon = random.nextGaussian() * gpsErrorStdDev
// 第2步:漂移(逻辑完全保留)
driftLat = driftLat * 0.8 + random.nextGaussian() * gpsErrorStdDev * driftWeight
driftLon = driftLon * 0.8 + random.nextGaussian() * gpsErrorStdDev * driftWeight
// 第3步:合并(逻辑完全保留)
val finalLon = rawLon + whiteNoiseLon + driftLon
val finalLat = rawLat + whiteNoiseLat + driftLat
val totalError = sqrt((whiteNoiseLon + driftLon).pow(2) + (whiteNoiseLat + driftLat).pow(2))
return Triple(finalLon, finalLat, totalError) // ✅ 返回噪声大小便于记录
}
}
新增 NoiseProcessor(协调多个噪声引擎):
class NoiseProcessor {
private val gpsNoiseEngine = GpsNoiseEngine()
// 预留位置:
// private val velocityNoiseEngine = VelocityNoiseEngine()
// private val accelerationNoiseEngine = AccelerationNoiseEngine()
fun applyNoise(rawCoord: RawCoordinate, physicsState: PhysicsState): NoisyCoordinate {
val (noisyLon, noisyLat, gpsError) = gpsNoiseEngine.applyNoise(
rawCoord.lon, rawCoord.lat, physicsState
)
// ✅ 预留位置:
// val velocityNoise = velocityNoiseEngine.applyNoise(physicsState.velocity)
// val accelNoise = accelerationNoiseEngine.applyNoise(physicsState.acceleration)
return NoisyCoordinate(noisyLon, noisyLat, gpsError)
}
}
改进:
- 返回值更明确(Triple 包含噪声大小)
- 创建 NoiseProcessor 协调多个噪声引擎
- GPS噪声算法逻辑完全保留
- 清晰标记了预留位置供未来扩展
总结
| 方面 | 原代码 | 新代码 |
|---|---|---|
| 项目独立性 | 修改原项目 | 新建独立项目,原项目不动 |
| 对象管理 | 每次重建 | 一次初始化,重复使用 |
| 流程清晰度 | 混乱 | 4步清晰流程 |
| 数据模型 | 不完整 | 完整的中间状态定义 |
| 命名规范 | 不规范 | 规范统一 |
| 注释文档 | 无 | 充分的伪代码和说明 |
| 扩展预留 | 无 | 清晰标记的预留位置 |
| 核心算法 | 保留 | 完全保留 |
如何使用新项目
- 快速上手:看 Main.kt(完整使用示例)
- 理解流程:看 DATA_FLOW_GUIDE.md(数据流参考)
- 要修改代码:看 README.md(各文件职责说明)
- 要加新功能:看 GpsNoiseEngine.kt(预留位置已标记)