设计模式之享元模式

发布时间 2023-12-05 00:10:55作者: 当时明月在曾照彩云归

1. 定义

共享多个对象所共有的相同状态,以节省内存和提高性能

2. 口语化举例

现有一台灯工厂,有五个生产线(分别生产五种产品)、两个销售部门

这两销售部门在销售产品出去后,都会直接去找生产线生产,有时两部门同时销售出同一种产品,导致这个产品的生产线繁忙

现在,工厂提出一种新方法:

每个生产线都会生产一部分产品,作为存货,销售部门需要拿货就直接去存货里找,没有再联系生产线

这样做的好处是,销售直接找存货,有存货就共用,没有就再生产,两个销售部门之间共享了产品

共享共同需要的部分,这就是享元模式(享:共享,元:对象)

3. 源码示例

Cesium.js是一个著名的、基于WebGL的GIS前端框架

基于WebGL就不得不提WebGL(OpenGL)的基础概念,比如Shader、Shader Program等

Cesium中有频繁的Shader Program创建操作,而Shader Program由Shader构建,源码中封装了ShaderCache,用于管理Shader的创建

当需要创建Shader Program时,就会向ShaderCache中查找是否已有一样的Shader缓存,有就直接共用,没有就创建并缓存

ShaderCache中获取Shader的代码示例如下:

/**
 * Returns a shader program from the cache, or creates and caches a new shader program,
 * given the GLSL vertex and fragment shader source and attribute locations.
 *
 * @param {object} options Object with the following properties:
 * @param {string|ShaderSource} options.vertexShaderSource The GLSL source for the vertex shader.
 * @param {string|ShaderSource} options.fragmentShaderSource The GLSL source for the fragment shader.
 * @param {object} options.attributeLocations Indices for the attribute inputs to the vertex shader.
 *
 * @returns {ShaderProgram} The cached or newly created shader program.
 */
ShaderCache.prototype.getShaderProgram = function (options) {
  // ...
  let cachedShader;
  if (defined(this._shaders[keyword])) {
    cachedShader = this._shaders[keyword];

    // No longer want to release this if it was previously released.
    delete this._shadersToRelease[keyword];
  } else {
    const context = this._context;

    const vertexShaderText = vertexShaderSource.createCombinedVertexShader(
      context
    );
    const fragmentShaderText = fragmentShaderSource.createCombinedFragmentShader(
      context
    );

    const shaderProgram = new ShaderProgram({
      gl: context._gl,
      logShaderCompilation: context.logShaderCompilation,
      debugShaders: context.debugShaders,
      vertexShaderSource: vertexShaderSource,
      vertexShaderText: vertexShaderText,
      fragmentShaderSource: fragmentShaderSource,
      fragmentShaderText: fragmentShaderText,
      attributeLocations: attributeLocations,
    });

    cachedShader = {
      cache: this,
      shaderProgram: shaderProgram,
      keyword: keyword,
      derivedKeywords: [],
      count: 0,
    };

    // A shader can't be in more than one cache.
    shaderProgram._cachedShader = cachedShader;
    this._shaders[keyword] = cachedShader;
    ++this._numberOfShaders;
  }

  ++cachedShader.count;
  return cachedShader.shaderProgram;
};

从示例代码可以知道,ShaderCache实现了Shader的复用、共用

4. 总结

4.1 设计优点

  • 节省大量内存

4.2 适用场景

  • 在程序创建大量对象时

5. 参考资料

[1] 享元设计模式 (refactoringguru.cn)

[2] cesium/packages/engine/Source/Renderer/ShaderCache.js at main · CesiumGS/cesium (github.com)