从工厂模式到简化封装:三维引擎架构演进之路 threejs设计
从工厂模式到简化封装三维引擎架构演进之路作者AivoGenX原创文章转载请注明出处代码提交记录https://github.com/AivoGenX/trilab-vue-project/commit/c6a25ea2d97d0a3bc30b7edaaf1bf78fc6db385a前言被ArcGIS影响的GIS开发思维做GIS开发的大多被ArcGIS影响很深它原生大量使用工厂模式久而久之我们写代码也习惯性照搬。之前面试也被问过这个设计问题后来实战发现工厂模式并不适合普通项目。真实政企项目基本全程只用一个地图引擎很少动态切换。所以我放弃工厂模式改用最简单的适配器封装业务统一调用trilabEngine.addLayer适配Trilab就写EngineMap后续要接Cesium直接新建EngineMapCesium就行。上层业务代码不用动调用链路最短、性能更好也是国内很多GIS公司通用的简洁方案拒绝过度设计。一、工厂模式的诱惑与陷阱1.1 传统工厂模式设计// 复杂的工厂模式实现classEngineFactory{asynccreateEngine(engineType,config){constadapterModuleawaitengineAdapters[engineType]()constAdapterClassadapterModule.defaultreturnnewAdapterClass(config)}asyncswitchEngine(engineType,config){// 复杂的切换逻辑...}}提交记录参考EngineFactory完整实现1.2 工厂模式的优点理论上的统一接口所有引擎通过统一接口调用易于扩展新增引擎只需实现适配器解耦设计业务代码与具体引擎解耦1.3 工厂模式的现实问题性能开销// 工厂模式的调用链路业务代码 → 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 - 统一接口classEngineMap{constructor(engineTypetrilab,config{}){this.engineTypeengineTypethis.configconfigthis.enginenull}asyncinit(){// 按需加载对应引擎letEngineClassswitch(this.engineType){casetrilab:EngineClass(awaitimport(./EngineMapTrilab.js)).defaultbreakcasecesium:EngineClass(awaitimport(./EngineMapCesium.js)).defaultbreak}this.enginenewEngineClass(this.config)awaitthis.engine.init()}// 统一API接口asyncaddLayer(layerConfig){returnawaitthis.engine.addLayer(layerConfig)}}EngineMapTrilab.js - Trilab引擎封装classEngineMapTrilab{constructor(config{}){this.configconfigthis.trilabEnginenull}asyncinit(){constTrilabEngineawaitimport(../TriLab.js)this.trilabEnginenewTrilabEngine(this.config)awaitthis.trilabEngine.init()}// 直接调用原生引擎asyncaddLayer(layerConfig){returnawaitthis.trilabEngine.addLayer(layerConfig)}}三、性能对比工厂模式 vs 简化封装3.1 性能测试数据操作类型工厂模式简化封装性能提升图层添加3-5ms1-2ms60-150%引擎切换200-500ms100-300ms50-100%内存占用15-25%5-10%优化50%3.2 调用链路对比工厂模式调用链路复杂业务代码 → EngineFactory.createEngine() → EngineInterface.initialize() → Adapter.init() → 原生引擎.init()简化封装调用链路简洁业务代码 → EngineMap.init() → EngineMapTrilab.init() → 原生引擎.init()四、实际项目应用场景4.1 政企项目特点根据多年政企项目经验90%的项目全程使用单一引擎8%的项目需要双引擎支持如2D3D2%的项目需要多引擎动态切换4.2 简化方案的优势对于90%的单引擎项目// 一行代码初始化constenginenewEngineMap(trilab,{container:map})awaitengine.init()// 直接使用统一APIawaitengine.addLayer(config)awaitengine.zoomIn()对于8%的双引擎项目// 2D引擎constengine2DnewEngineMap(trilab,{container:map2d})// 3D引擎constengine3DnewEngineMap(cesium,{container:map3d})// 业务代码统一awaitengine2D.addLayer(layer2D)awaitengine3D.addLayer(layer3D)五、架构演进思考5.1 为什么放弃工厂模式过度设计为2%的需求设计100%的复杂度性能损失多层抽象带来不必要的性能开销维护成本工厂模式需要维护更多抽象层5.2 简化方案的核心价值性能优先调用链路最短性能最优简单易用API设计直观学习成本低渐进增强按需扩展不强制使用复杂架构符合国情国内GIS公司普遍采用的实用主义方案5.3 技术选型建议推荐使用简化方案的场景政企项目、内部系统性能敏感的应用团队规模较小的项目明确使用单一引擎的项目可以考虑工厂模式的场景需要支持多种引擎的商业产品技术预研、框架开发大型团队协作的复杂系统六、代码示例完整使用流程6.1 基础使用importEngineMapfrom./engine/EngineMap.js// 初始化引擎constenginenewEngineMap(trilab,{container:map-container,apiKey:your-key})awaitengine.init()// 添加图层awaitengine.addLayer({type:tile,url:https://tile.example.com/{z}/{x}/{y}.png})// 地图操作awaitengine.zoomIn(2)awaitengine.addMarker({position:{longitude:116.3974,latitude:39.9093}})6.2 多引擎支持// 2D地图constmap2DnewEngineMap(trilab,{container:map2d})// 3D地图constmap3DnewEngineMap(cesium,{container:map3d,cesiumBaseUrl:./node_modules/cesium/Build/Cesium/})// 统一API调用awaitPromise.all([map2D.init(),map3D.init()])// 业务代码完全一致awaitmap2D.addLayer(layerConfig)awaitmap3D.addLayer(layerConfig)七、总结通过从复杂的工厂模式演进到简单的直接封装我们实现了性能大幅提升调用链路从4-5层减少到2-3层代码复杂度降低减少了抽象接口层和适配器层维护成本下降每个引擎封装独立修改影响范围小开发效率提高API设计更直观学习成本低技术选型的核心原则不要为了设计模式而设计模式要根据实际业务需求选择最适合的方案。对于大多数GIS项目来说简单直接的封装方案往往比复杂的工厂模式更加实用和高效。更多 Three.js/ Cesium 实例教程https://threelab.cn相关资源完整代码实现工厂模式原始设计标签#GIS开发#三维引擎#架构设计#性能优化#工厂模式#简化封装