从工厂模式到简化封装:三维引擎架构演进之路 threejs设计

从工厂模式到简化封装:三维引擎架构演进之路

作者: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 为什么放弃工厂模式?

  1. 过度设计:为2%的需求设计100%的复杂度
  2. 性能损失:多层抽象带来不必要的性能开销
  3. 维护成本:工厂模式需要维护更多抽象层

5.2 简化方案的核心价值

  1. 性能优先:调用链路最短,性能最优
  2. 简单易用:API设计直观,学习成本低
  3. 渐进增强:按需扩展,不强制使用复杂架构
  4. 符合国情:国内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)

七、总结

通过从复杂的工厂模式演进到简单的直接封装,我们实现了:

  1. 性能大幅提升:调用链路从4-5层减少到2-3层
  2. 代码复杂度降低:减少了抽象接口层和适配器层
  3. 维护成本下降:每个引擎封装独立,修改影响范围小
  4. 开发效率提高:API设计更直观,学习成本低

技术选型的核心原则:不要为了设计模式而设计模式,要根据实际业务需求选择最适合的方案。对于大多数GIS项目来说,简单直接的封装方案往往比复杂的工厂模式更加实用和高效。


相关资源

标签#GIS开发 #三维引擎 #架构设计 #性能优化 #工厂模式 #简化封装

相关推荐
To_OC5 小时前
我被 useState 坑了两次之后,终于把它的脾气摸透了
前端·javascript·react.js
煎饼学大模型6 小时前
Agent 的“大脑-手“解耦架构:当推理层和工具执行层各自独立演进
数据库·人工智能·oracle·架构·agent
a1117769 小时前
中国古建筑瑰宝 3D THreeJS 开源
3d
kyriewen10 小时前
我review了一份Vibe Coding写的前端代码——能跑,但5个地方迟早要命
前端·javascript·ai编程
Hilaku13 小时前
为什么在 2026 年,MPA(多页面应用)正在悄悄复辟?
前端·javascript·程序员
悟空瞎说13 小时前
iOS 高效绘图
javascript
韩楚风13 小时前
【参天引擎】一次宕机后的数据恢复,让我把 Cantian 持久化与恢复的六大机制全搞明白了
服务器·网络·数据库·分布式·mysql·架构·cantian
Slice_cy13 小时前
Mint 自研框架设计与实现:从重复开发走向配置驱动(五)
前端·后端·架构
heimeiyingwang13 小时前
【架构实战】Helm Chart 进阶:依赖管理、测试与 CI/CD 集成
ci/cd·架构
四眼肥鱼13 小时前
【Nextjs】macos 系统运行报错:Error: Cannot find module '../lightningcss.darwin-x64.node'
前端·架构·前端框架