结构型模式,如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。
从本小节开始,我们就来开始学习结构型设计模式了 首先我们先学习第一种:享元模式,也是比较简单的一种结构型设计模式
享元模式的定义
什么是享元模式呢?还是可以“忘文生义”,享就是共享,元就是元素 共享的元素的意思 ,那么如何来共享元素呢?很容易想到,共享池,了解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
}
}
执行的结果如下:
这是点击事件