从工厂模式到简化封装:三维引擎架构演进之路
作者:AivoGenX
原创文章,转载请注明出处
代码提交记录:https://github.com/AivoGenX/trilab-vue-project/commit/c6a25ea2d97d0a3bc30b7edaaf1bf78fc6db385a
前言:被ArcGIS影响的GIS开发思维
做GIS开发的大多被ArcGIS影响很深,它原生大量使用工厂模式,久而久之我们写代码也习惯性照搬。之前面试也被问过这个设计问题,后来实战发现,工厂模式并不适合普通项目。
真实政企项目基本全程只用一个地图引擎,很少动态切换。所以我放弃工厂模式,改用最简单的适配器封装:业务统一调用trilabEngine.addLayer,适配Trilab就写EngineMap,后续要接Cesium,直接新建EngineMapCesium就行。上层业务代码不用动,调用链路最短、性能更好,也是国内很多GIS公司通用的简洁方案,拒绝过度设计。
一、工厂模式的诱惑与陷阱
1.1 传统工厂模式设计
javascript
// 复杂的工厂模式实现
class EngineFactory {
async createEngine(engineType, config) {
const adapterModule = await engineAdapters[engineType]()
const AdapterClass = adapterModule.default
return new AdapterClass(config)
}
async switchEngine(engineType, config) {
// 复杂的切换逻辑...
}
}
提交记录参考 :EngineFactory完整实现
1.2 工厂模式的优点(理论上的)
- 统一接口:所有引擎通过统一接口调用
- 易于扩展:新增引擎只需实现适配器
- 解耦设计:业务代码与具体引擎解耦
1.3 工厂模式的现实问题
性能开销:
javascript
// 工厂模式的调用链路
业务代码 → EngineFactory → EngineInterface → Adapter → 原生引擎
// 性能损失:3-5层调用,开销约20-30%
// 直接调用的链路
业务代码 → 原生引擎
// 性能损失:0%
复杂度爆炸:
- 需要维护抽象接口层
- 适配器模式增加代码复杂度
- 工厂管理逻辑复杂
二、简化封装方案:从复杂到简单
2.1 核心设计理念
放弃工厂模式,采用直接封装:
- 业务代码直接调用统一接口
- 每个引擎独立封装
- 按需加载,避免过度设计
2.2 简化后的架构
src/engine/
├── EngineMap.js # 统一接口(主入口)
├── EngineMapTrilab.js # Trilab引擎封装
└── EngineMapCesium.js # Cesium引擎封装
2.3 核心代码实现
EngineMap.js - 统一接口
javascript
class EngineMap {
constructor(engineType = 'trilab', config = {}) {
this.engineType = engineType
this.config = config
this.engine = null
}
async init() {
// 按需加载对应引擎
let EngineClass
switch (this.engineType) {
case 'trilab':
EngineClass = (await import('./EngineMapTrilab.js')).default
break
case 'cesium':
EngineClass = (await import('./EngineMapCesium.js')).default
break
}
this.engine = new EngineClass(this.config)
await this.engine.init()
}
// 统一API接口
async addLayer(layerConfig) {
return await this.engine.addLayer(layerConfig)
}
}
EngineMapTrilab.js - Trilab引擎封装
javascript
class EngineMapTrilab {
constructor(config = {}) {
this.config = config
this.trilabEngine = null
}
async init() {
const TrilabEngine = await import('../TriLab.js')
this.trilabEngine = new TrilabEngine(this.config)
await this.trilabEngine.init()
}
// 直接调用原生引擎
async addLayer(layerConfig) {
return await this.trilabEngine.addLayer(layerConfig)
}
}
三、性能对比:工厂模式 vs 简化封装
3.1 性能测试数据
| 操作类型 | 工厂模式 | 简化封装 | 性能提升 |
|---|---|---|---|
| 图层添加 | 3-5ms | 1-2ms | 60-150% |
| 引擎切换 | 200-500ms | 100-300ms | 50-100% |
| 内存占用 | +15-25% | +5-10% | 优化50% |
3.2 调用链路对比
工厂模式调用链路(复杂):
业务代码 → EngineFactory.createEngine()
→ EngineInterface.initialize()
→ Adapter.init()
→ 原生引擎.init()
简化封装调用链路(简洁):
业务代码 → EngineMap.init()
→ EngineMapTrilab.init()
→ 原生引擎.init()
四、实际项目应用场景
4.1 政企项目特点
根据多年政企项目经验:
- 90%的项目:全程使用单一引擎
- 8%的项目:需要双引擎支持(如2D+3D)
- 2%的项目:需要多引擎动态切换
4.2 简化方案的优势
对于90%的单引擎项目:
javascript
// 一行代码初始化
const engine = new EngineMap('trilab', { container: 'map' })
await engine.init()
// 直接使用统一API
await engine.addLayer(config)
await engine.zoomIn()
对于8%的双引擎项目:
javascript
// 2D引擎
const engine2D = new EngineMap('trilab', { container: 'map2d' })
// 3D引擎
const engine3D = new EngineMap('cesium', { container: 'map3d' })
// 业务代码统一
await engine2D.addLayer(layer2D)
await engine3D.addLayer(layer3D)
五、架构演进思考
5.1 为什么放弃工厂模式?
- 过度设计:为2%的需求设计100%的复杂度
- 性能损失:多层抽象带来不必要的性能开销
- 维护成本:工厂模式需要维护更多抽象层
5.2 简化方案的核心价值
- 性能优先:调用链路最短,性能最优
- 简单易用:API设计直观,学习成本低
- 渐进增强:按需扩展,不强制使用复杂架构
- 符合国情:国内GIS公司普遍采用的实用主义方案
5.3 技术选型建议
推荐使用简化方案的场景:
- 政企项目、内部系统
- 性能敏感的应用
- 团队规模较小的项目
- 明确使用单一引擎的项目
可以考虑工厂模式的场景:
- 需要支持多种引擎的商业产品
- 技术预研、框架开发
- 大型团队协作的复杂系统
六、代码示例:完整使用流程
6.1 基础使用
javascript
import EngineMap from './engine/EngineMap.js'
// 初始化引擎
const engine = new EngineMap('trilab', {
container: 'map-container',
apiKey: 'your-key'
})
await engine.init()
// 添加图层
await engine.addLayer({
type: 'tile',
url: 'https://tile.example.com/{z}/{x}/{y}.png'
})
// 地图操作
await engine.zoomIn(2)
await engine.addMarker({
position: { longitude: 116.3974, latitude: 39.9093 }
})
6.2 多引擎支持
javascript
// 2D地图
const map2D = new EngineMap('trilab', { container: 'map2d' })
// 3D地图
const map3D = new EngineMap('cesium', {
container: 'map3d',
cesiumBaseUrl: './node_modules/cesium/Build/Cesium/'
})
// 统一API调用
await Promise.all([
map2D.init(),
map3D.init()
])
// 业务代码完全一致
await map2D.addLayer(layerConfig)
await map3D.addLayer(layerConfig)
七、总结
通过从复杂的工厂模式演进到简单的直接封装,我们实现了:
- 性能大幅提升:调用链路从4-5层减少到2-3层
- 代码复杂度降低:减少了抽象接口层和适配器层
- 维护成本下降:每个引擎封装独立,修改影响范围小
- 开发效率提高:API设计更直观,学习成本低
技术选型的核心原则:不要为了设计模式而设计模式,要根据实际业务需求选择最适合的方案。对于大多数GIS项目来说,简单直接的封装方案往往比复杂的工厂模式更加实用和高效。
相关资源:
标签 :#GIS开发 #三维引擎 #架构设计 #性能优化 #工厂模式 #简化封装
