这一周都是面试。昨天被问起设计模式,好久没去研究这方面了,我回忆着之前在《js设计模式》上看到的东西说了下自己对原型模式、工厂模式和享元模式的理解,一开始hr还点了点头,然后指出我对享元模式的理解不到位。然后开始说他的看法。
我最喜欢面试的时候这种交流,不是提醒自己忘记的东西,就是通过别人的认识去理解某些事物。
一开始我还挺听得懂,后来他说享元模式在某种意义上就是单例模式+工厂模式的结合时,我就有点迷糊了。
我回忆了一下单例和工厂,试图寻找他们和享元的共同点,忽然听到面试官说“享元池”。
我愣了一下,这个概念之前好像没接触过,也可能以前看书的时候见过但是已经忘了。额大哥你说慢点。。。
我立马联想到系统的线程池,寻思这个“池”的概念是否也有相似的意味。。。
等一下啊,大哥你刚才说了什么啊我没认真听啊喂。。
然后我似懂非懂地点了点头,后面还说了点什么但我已经迷糊了。。。
然后面试官补了一句“这个还是看应用场景”
我:“嗯。。。我的认识还是比较粗浅T.T”
----废话结束----
然后现在来梳理一下,单例模式的定义其实就一句话:“保证一个类仅有一个实例,并提供一个访问它的全局访问点”。
因此只要符合这个定义,都属于单例,这也意味着,一个类在符合这个定义的同时,也可以符合其他特性。就像一个女人有了孩子,她就是一个母亲,但她也同时可以是一位教师。
现在来看看享元模式:
1.定义:
维基上说,享元模式是用来设计灵活、可重用的面向对象程序的(太抽象了)。Graphic Design Patterns中对该模式的动机是这样描述的:“但在很多情况下需要在系统中增加类和对象的个数。当对象数量太多时,将导致运行代价过高,带来性能下降等问题。。。享元模式正是为解决这一类问题而诞生的。享元模式通过共享技术实现相同或相似对象的重用。”
因此共享是该模式的核心,重用是手段,最终目的还是减少内存资源的使用。
“享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。”
什么是粗粒度与细粒度,他们的区别又是什么?
我个人认为所谓细粒度对象,就是用来实现某些具体逻辑/操作的对象,这些对象一般比较轻量,功能不复杂。如果系统需要创建多个这样的对象,且这些对象只有一些细微的差别,我们可以将共同部分抽离出来进行标准化产生一个标准化的对象保存在某个地方以复用(保存标准化对象的地方就叫享元池),将那有差别的部分作为外部信号传给它,作为其活动的依据。当已存有的对象与需要的对象不匹配时,再创建新的对象保存供以后复用。
2.享元模式是一种对象结构型设计模式
“对象结构型模式关心类与对象的组合,通过关联关系使得在一 个类中定义另一个类的实例对象,然后通过该对象调用其方法。 根据‘合成复用原则’,在系统中尽量使用关联关系来替代继 承关系,因此大部分结构型模式都是对象结构型模式。”
也就是说,要实现享元模式,需要两个以上类与对象的配合,下面是该模式的模式结构:
要在系统中使用享元模式,往往会定义一个享元工厂(工厂模式),这个工厂做两件事情:
1.在里面维护一个享元池(这个享元池就是保存标准化对象的地方,其实是一张哈希表,同时也是一个实例)
2.暴露一个方法getFlyweight(key):在享元池中遍历key,返回匹配的对象。若不存在则创建新对象(key),保存到享元池,然后返回该对象。(这些标准化对象就是所谓的享元对象)
这就是为什么面试官跟我说享元模式也是单例+工厂的结合。
在菜鸟教程和tutorialspoint中用了同一个java例子来展示享元的实现:例子假设有个需求是要画20个圈圈,其中每个圈圈的圆心、半径是不确定的,只知道这些圈圈有五种颜色。
如果用不科学的方法的话,就是创建20个圈圈对象,每个对象接受颜色、半径、圆心作为参数,然后调用draw()方法画出来。
如果使用享元模式,我们可以在创建圈圈类之后,创建一个享元工厂,里面定义一张哈希表,每次要创建圈圈实例时,先将颜色参数传给享元工厂,在哈希表中创建并返回带某种颜色的圈圈实例。以后每次要画同种颜色的圈圈,就查看哈希表中是否有该颜色的实例,有的话就复用这个享元对象(接收半径和圆心参数,调用draw方法画圈圈),没有的话就创建新的享元对象。
原本需要创建20个实例,现在其实只创建了5个。
当然,这个例子只是展示享元模式的实现,并没有实际意义。讲真,目前只知道这种模式在编辑器中用的比较多(处理字符串)。我估计前端以后用到的机会可能也不大。。。