定义:
运用共享技术有效地支持大量细粒度的对象。
在享元对象内部并且不会随环境改变而改变的共享部分,称为享元对象的内部状态。
随换将改变而改变,不可以共享的状态,称为外部状态。
优点:
可以避免大量非常相似类的开销。因为有了共享对象,实例总数就大大减少了,共享的对象越多,存储节约也就也多,节约量随着共享状态的增多而增大。
如果共享其相关的代码、数据,使服务器资源达成共享,减少服务器资源(硬盘、内存、cpu、数据库控件等)的浪费。
在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够大幅度地减少需要实例化的类的数量。
原理:
通过把那些参数移到类实例的外面,在方法调用时将他们传递进来,就可以同构大幅度地减少单个实例的数量来节省服务器的资源(硬盘、内存、cpu、数据库控件等)。
享元模式Flyweight执行时所需的状态是有内部的也可能有外部的。内部状态存储于ConcreteFlyweight对象之中;而外部对象则应考虑由客户端对象存储、计算。当调用Flyweight对象的操作时,将该状态传递给它。(不同网站客户的账号)
使用环境:
在程序设计中,有是需要生成大量细粒度的类实例来表示数据。如果这些实例除了几个参数外基本上都是相同的。则通过享元模式可以大幅度地减少实例化的类的数量。
如果一个程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用享元模式。
对象的大多数状态可以是外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此事可以考虑使用享元模式。
现实生活中的应用,某些情况下,对象的数量可能会太多,从而导致了运行时的资源、性能损耗。通过享元模式,运用共享技术可以米面大量细粒度的对象,同时不影响客户端。
享元模式多用于一种低层的一种设计模式。
例如:
1..NET中,字符串string就是运用了Flyweight模式,Object.ReferenceEquals(Object a,Object b)方法就是用来确定a、b是否相同的实例。下面结果返回为true
2.棋子对象(围棋、五子棋、跳棋……),颜色为内部状态,方位坐标为外部状态。围棋有361个位置,如果产生这么多对象,一台服务器很难支撑,内存空间有限。使用享元模式,棋子对象可以减少到2个。
缺点:
需要维护一个记录了系统已有的搜有享元的列表,本身需要耗费资源,也使得系统更加复杂。
为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。
Tip:
共享也要体现对象间的不同,而不是只体现它们共享的部分。
应当在有足够多的对象实例可供共享时,才值得使用享元模式。
结构图:
FlyweightFactory
客户端:
结果:
FlyweightFactory根据客户需求返回早已生成好的对象,但一定要事先生成对象实例?
FlyweightFactory完全可以初始化时什么也不做,到需要时,再去判断对象是否为null来决定是否实例化。
为什么要有UnsharedConcreteFlyweight的存在?
尽管大部分时间需要共享对象来降低内存的损耗,单个别时候可能不需要共享。此时UnsharedConcreteFlyweight子类就有存在的必要了,它可以解决那些不需要共享对象的问题。
示例:
网站(产品展示、博客、新闻……),每种都需要多个,网站结构详细,但本质一样。如果每个需求一个实例(相当于一个相同网站的实例选项很多),服务器资源(硬盘、内存、cpu、数据库控件等)浪费。如果共享其相关的代码、数据,使服务器资源达成共享,减少服务器资源。对于代码来说,由于一份实例,扩展、维护更加容易。
用户类,用于网站的客户账号,是“网站”类的外部状态
网站抽象类
网站具体类:
网站工厂类
客户端
结果: