天天一个设想形式之享元形式

作者按:《天天一个设想形式》旨在开端体会设想形式的精华,现在采纳
javascript
python两种言语完成。固然,每种设想形式都有多种完成体式格局,但此小册只纪录最直接了当的完成体式格局 :)

个人手艺博客-godbmw.com 迎接来玩! 每周最少 1 篇原创手艺分享,另有开源教程(webpack、设想形式)、口试刷题(偏前端)、学问整顿(每周细碎),迎接历久关注!本篇博客地点是:《天天一个设想形式之享元形式》

假如您也想举行学问整顿 + 搭建功用完美/设想简约/疾速启动的个人博客,请直接戳theme-bmw

0. 项目地点

1. 什么是“享元形式”?

享元形式:运用同享手艺来削减建立对象的数目,从而削减内存占用、进步机能。

  1. 享元形式提示我们将一个对象的属性划分为内部和外部状况

    • 内部状况:能够被对象鸠合同享,一般不会转变
    • 外部状况:依据运用场景常常转变
  2. 享元形式是应用时候调换空间的优化形式。

2. 运用场景

享元形式虽然名字听起来比较深邃,然则实际运用异常轻易:只如果须要大批建立反复的类的代码块,均能够运用享元形式抽离内部/外部状况,削减反复类的建立。

为了显现它的壮大,下面的代码是简朴地完成了人人耳熟能详的“对象池”,以彰显这类设想形式的魅力。

3. 代码完成

这里应用pythonjavascript完成了一个“通用对象池”类–ObjectPool。这个类治理一个装载余暇对象的数组,假如外部须要一个对象,直接从对象池中猎取,而不是经由过程new操纵

对象池能够大批削减反复建立雷同的对象,从而节省了体系内存,进步运转效力。

为了抽象申明“享元形式”在“对象池”完成和运用,迥殊预备了模仿了File类,而且模仿了“文件下载”操纵。

经由过程浏览下方代码能够发明:关于File类,内部状况是pool属性和download要领;外部状况是namesrc(文件名和文件链接)。借助对象池,完成了File类的复用。

注:为了轻易演示,Javascript完成的是并发操纵,Python完成的是串行操纵。输出效果略有不同。

3.1 Python3 完成

from time import sleep


class ObjectPool:  # 通用对象池
    def __init__(self):
        self.__pool = []

    # 建立对象
    def create(self, Obj):
        # 对象池中没有余暇对象,则建立一个新的对象
        # 对象池中有余暇对象,直接掏出,无需再次建立
        return self.__pool.pop() if len(self.__pool) > 0 else Obj(self)

    # 对象接纳
    def recover(self, obj):
        return self.__pool.append(obj)

    # 对象池大小
    def size(self):
        return len(self.__pool)


class File:  # 模仿文件对象
    def __init__(self, pool):
        self.__pool = pool

    def download(self):  # 模仿下载操纵
        print('+ 从', self.src, '最先下载', self.name)
        sleep(0.1)
        print('-', self.name, '下载完成')
        # 下载终了后,将对象从新放入对象池
        self.__pool.recover(self)


if __name__ == '__main__':
    obj_pool = ObjectPool()

    file1 = obj_pool.create(File)
    file1.name = '文件1'
    file1.src = 'https://download1.com'
    file1.download()

    file2 = obj_pool.create(File)
    file2.name = '文件2'
    file2.src = 'https://download2.com'
    file2.download()

    file3 = obj_pool.create(File)
    file3.name = '文件3'
    file3.src = 'https://download3.com'
    file3.download()

    print('*' * 20)
    print('下载了3个文件, 但实在只建立了', obj_pool.size(), '个对象')

输出效果(这里为了轻易演示直接运用了sleep要领,没有再用多线程模仿):

+ 从 https://download1.com 最先下载 文件1
- 文件1 下载完成
+ 从 https://download2.com 最先下载 文件2
- 文件2 下载完成
+ 从 https://download3.com 最先下载 文件3
- 文件3 下载完成
********************
下载了3个文件, 但实在只建立了 1 个对象

3.2 ES6 完成

// 对象池
class ObjectPool {
  constructor() {
    this._pool = []; //
  }

  // 建立对象
  create(Obj) {
    return this._pool.length === 0
      ? new Obj(this) // 对象池中没有余暇对象,则建立一个新的对象
      : this._pool.shift(); // 对象池中有余暇对象,直接掏出,无需再次建立
  }

  // 对象接纳
  recover(obj) {
    return this._pool.push(obj);
  }

  // 对象池大小
  size() {
    return this._pool.length;
  }
}

// 模仿文件对象
class File {
  constructor(pool) {
    this.pool = pool;
  }

  // 模仿下载操纵
  download() {
    console.log(`+ 从 ${this.src} 最先下载 ${this.name}`);
    setTimeout(() => {
      console.log(`- ${this.name} 下载终了`); // 下载终了后, 将对象从新放入对象池
      this.pool.recover(this);
    }, 100);
  }
}

/****************** 以下是测试函数 **********************/

let objPool = new ObjectPool();

let file1 = objPool.create(File);
file1.name = "文件1";
file1.src = "https://download1.com";
file1.download();

let file2 = objPool.create(File);
file2.name = "文件2";
file2.src = "https://download2.com";
file2.download();

setTimeout(() => {
  let file3 = objPool.create(File);
  file3.name = "文件3";
  file3.src = "https://download3.com";
  file3.download();
}, 200);

setTimeout(
  () =>
    console.log(
      `${"*".repeat(50)}\n下载了3个文件,但实在只建立了${objPool.size()}个对象`
    ),
  1000
);

输出效果以下:

+ 从 https://download1.com 最先下载 文件1
+ 从 https://download2.com 最先下载 文件2
- 文件1 下载终了
- 文件2 下载终了
+ 从 https://download3.com 最先下载 文件3
- 文件3 下载终了
**************************************************
下载了3个文件,但实在只建立了2个对象

4. 参考

  • 《JavaScript 设想形式和开辟实践》
    原文作者:心谭
    原文地址: https://segmentfault.com/a/1190000017457054
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞