05 JavasScript结构型设计模式之 - 享元模式
待兔 640 3

结构型模式,如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。

从本小节开始,我们就来开始学习结构型设计模式了 首先我们先学习第一种:享元模式,也是比较简单的一种结构型设计模式

享元模式的定义

什么是享元模式呢?还是可以“忘文生义”,享就是共享,元就是元素 共享的元素的意思 ,那么如何来共享元素呢?很容易想到,共享池,了解java的同学应该知道 ,java 里面有个线程池,对了,就是这样。

下面给出一个正式一点的定义 使用共享对象池来缓存对象,如果有空闲的对象有拿来用,如果没有,新建一个放入池中,然后再返回使用。

享元模式的作用

享元模式的主要作用就是可以对象共享,避免过多的创建对象,这样就可以减少内存的使用。正是因为有了这个特点,享元模式适合应用在大量创建重复对象的场景,来缓存可共享的对象。

这样一来,减少了对象的创建,既节省了内存,也提高了性能

下面几个场景需要用到享元模式

  • 系统中存在大量的相似对象
  • 需要缓冲池的场景
  • 消息队列中也有用到享元模式

下面我们就举一个简单的例子来揭示享元模式的用法

首先需要有一个抽象的类,也就是享元对象。 比如dom事件分很多种,有点击事件,有长按事件,有右击事件,有键盘事件等等 这些事件有一个共同的父类,我们起名叫: Event ,这是一个抽象类 所谓的抽象类,就是不能实例对象的类,只能作为其它类的父类使用

js 里面没有抽象类这个概念的,java里面是有一个关键字的abstract可以定义抽象类

那么,在 js 里面,我们怎么定义一个抽象类,不用实例化对象呢? 管它有没有关键字,我们只要能达到目的就行,只要知道原理了,实现有多种方法

可以利用 new.target 来实现类似Java的抽象类

如果构造函数不是通过new命令调用的,new.target 会返回undefined, 因此这个属性可以用来确定构造函数是怎么调用的。 子类继承父类时,new.target会返回子类。

Event 的源码如下:

//抽象类,不能实例化
class Event {
    constructor(){
        if(new.target === Event){
            throw new('本类不能被实例化')
        }
    }

    doSomething(){
        console.log('在这里做一些事')
    }
}

上面源码可以看出,我们在构造函数里面使用new.target来防止直接通过new一个对象,从而达到定义抽象类的目的


有了基类后,我们需要定义一个具体的类,比如我们定义一个点击事件 ClickEvent 源码如下:

//具体的类
class ClickEvent extends Event{
    constructor(){
        super()
    }

    doSomething(){
        console.log('这是点击事件')
    }

}

代码很简单,没有什么好说的,这里有一点要注意,就是构造函数里面必须调用父类的构造函数


下面我们来定义一个对象池,或者叫事件工厂,对外暴露一个方法,用来获取事件的

同时也要有一个变量保存共享的对象,用 Map 结构最适合了

源码如下 :

//定义共享池
class EventFactory{
    constructor(){
        //事件池,也就是定上面定义中说的对象池
        //js 里面是弱类型,在java里面必须指定类型的,如  this.events = new HashMap<String,Event>
        //所以我们才需要定义一个 Event类,js中完全可以省略定义Event类,但是最好知道这个道理 
        //因为 ts 里面是有明确类型的
        this.events = new Map()
    }

    //从对象池中获取对象
    getEvent(key){

        //如果池中有,就返回
        if(this.events.get(key)){
            return this.events.get(key)
        }

        //如果池中没有,创建一个,放入池中,并返回
        let event = new ClickEvent()
        this.events.set(key,event)
        return event
    }
}

如上面的代码注释:如果有,直接返回,如果池中没有,新建一个享元对象,然后放入池中,再返回新建的对象。

这就是享元模式的思想,很简单,却很有用

我们贴出完整的源码,包括测试代码,如下:

'use strict';

//抽象类,不能实例化
class Event {
    constructor(){
        if(new.target === Event){
            throw new('本类不能被实例化')
        }
    }

    doSomething(){
        console.log('在这里做一些事')
    }
}

//具体的类
class ClickEvent extends Event{
    constructor(){
        super()
    }

    doSomething(){
        console.log('这是点击事件')
    }

}

//定义共享池
class EventFactory{
    constructor(){
        //事件池,也就是定上面定义中说的对象池
        //js 里面是弱类型,在java里面必须指定类型的,如  this.events = new HashMap<String,Event>
        //所以我们才需要定义一个 Event类,js中完全可以省略定义Event类,但是最好知道这个道理 
        //因为 ts 里面是有明确类型的
        this.events = new Map()
    }

    //从对象池中获取对象
    getEvent(key){

        //如果池中有,就返回
        if(this.events.get(key)){
            return this.events.get(key)
        }

        //如果池中没有,创建一个,放入池中,并返回
        let event = new ClickEvent()
        this.events.set(key,event)
        return event
    }
}

执行的结果如下:

这是点击事件

评论区

索引目录