NIUCLOUD是一款SaaS管理后台框架多应用插件+云编译。上千名开发者、服务商正在积极拥抱开发者生态。欢迎开发者们免费入驻。一起助力发展! 广告
# 享元模式 ## **享元模式的定义与特点** **享元(Flyweight)模式定义:**: - 运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的又橡来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。 > 面向对象技术可以很好地解决一些灵活性或可扩展性问题,但在很多情况下需要在系统中增加类和对象的个数。当对象数量太多时,将导致运行代价过高,带来性能下降等问题。享元模式正是为解决这一类问题而诞生的。享元模式通过共享技术实现相同或相似对象的重用。 **主要优点:** - 相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。 **其主要缺点是:** - 为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。 - 读取享元模式的外部状态会使得运行时间稍微变长。 ## **享元模式适用场景:** 享元模式是通过减少内存中对象的数量来节省内存空间的,所以以下几种情形适合采用享元模式。 - 一个应用程序使用了大量相同或相似的对象,这些对象耗费大量的内存资源,造成很大的存储开销 - 对象的大多数状态都可变为外部状态 - 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象 - 应用程序不依赖于对象标识 - 大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。 - 由于享元模式需要额外维护一个保存享元的[数据结构](http://c.biancheng.net/data_structure/),所以应当在有足够多的享元实例时才值得使用享元模式。 ## **应用:** - String常量池 系统底层的设计。例如字符串的创建。如果两个字符串相同,则不会创建第二个字符串,而是第二个的引用直接指向第一个字符串。$str1=”abc”,$str2=”abc”.则内存存储中只会创建一个字符串“abc”而引用$str1.$str2都会指向它。 - 数据库连接池 ## **模式的结构** 享元模式的主要角色有如下。 1. 抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。 2. 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。 3. 非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。 4. 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。 ![](https://img.kancloud.cn/82/a6/82a68517d472c96c69aa71e1c2e96cf0_767x532.png) ``` <pre class="calibre10">``` <span class="token">//不共享的具体享元角色,客户端直接调用</span> class <span class="token4">UnsharedConcreteFlyweight</span><span class="token3">{</span> private $info<span class="token3">;</span> <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>$info<span class="token3">)</span><span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>info <span class="token1">=</span> $info<span class="token3">;</span> <span class="token3">}</span> <span class="token5">function</span> <span class="token4">getInfo</span><span class="token3">(</span><span class="token3">)</span><span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>info<span class="token3">;</span> <span class="token3">}</span> <span class="token5">function</span> <span class="token4">setInfo</span><span class="token3">(</span>$info<span class="token3">)</span><span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>info<span class="token1">=</span>$info<span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//抽象享元角色</span> interface <span class="token4">Flyweight</span><span class="token3">{</span> <span class="token5">function</span> <span class="token4">operation</span><span class="token3">(</span>UnsharedConcreteFlyweight $state<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//共享的具体享元角色1</span> class <span class="token4">ConcreteFlyweight</span> implements <span class="token4">Flyweight</span><span class="token3">{</span> private $key<span class="token3">;</span> <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>String $key<span class="token3">)</span><span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>key <span class="token1">=</span> $key<span class="token3">;</span> echo <span class="token2">"具体享元"</span><span class="token3">.</span>$key<span class="token3">.</span><span class="token2">"被创建!<br>"</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">function</span> <span class="token4">operation</span><span class="token3">(</span>UnsharedConcreteFlyweight $outState<span class="token3">)</span><span class="token3">{</span> echo <span class="token2">"具体享元"</span><span class="token3">.</span>$this<span class="token1">-</span><span class="token1">></span>key<span class="token3">.</span><span class="token2">"被调用!"</span><span class="token3">;</span> echo <span class="token2">"非享元信息是:"</span><span class="token3">.</span>$outState<span class="token1">-</span><span class="token1">></span><span class="token4">getInfo</span><span class="token3">(</span><span class="token3">)</span><span class="token3">.</span><span class="token2">"<br>"</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//享元工厂模式</span> class <span class="token4">FlyweightFactory</span><span class="token3">{</span> private $flyweights <span class="token1">=</span> <span class="token4">array</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token5">function</span> <span class="token4">getFlyweight</span><span class="token3">(</span>$key<span class="token3">)</span><span class="token3">{</span> <span class="token5">if</span><span class="token3">(</span><span class="token1">!</span><span class="token4">isset</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>flyweights<span class="token3">[</span>$key<span class="token3">]</span><span class="token3">)</span><span class="token3">)</span><span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>flyweights<span class="token3">[</span>$key<span class="token3">]</span><span class="token1">=</span><span class="token5">new</span> <span class="token4">ConcreteFlyweight</span><span class="token3">(</span>$key<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> echo <span class="token2">"具体享元"</span><span class="token3">.</span>$state<span class="token3">.</span><span class="token2">"已经存在,被成功获取!<br>"</span><span class="token3">;</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>flyweights<span class="token3">[</span>$key<span class="token3">]</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//测试</span> $flyweightFactory <span class="token1">=</span> <span class="token5">new</span> <span class="token4">FlyweightFactory</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $flyweight1 <span class="token1">=</span> $flyweightFactory<span class="token1">-</span><span class="token1">></span><span class="token4">getFlyweight</span><span class="token3">(</span><span class="token2">"state A"</span><span class="token3">)</span><span class="token3">;</span> $flyweight2 <span class="token1">=</span> $flyweightFactory<span class="token1">-</span><span class="token1">></span><span class="token4">getFlyweight</span><span class="token3">(</span><span class="token2">"state A"</span><span class="token3">)</span><span class="token3">;</span> $flyweight3 <span class="token1">=</span> $flyweightFactory<span class="token1">-</span><span class="token1">></span><span class="token4">getFlyweight</span><span class="token3">(</span><span class="token2">"state A"</span><span class="token3">)</span><span class="token3">;</span> $flyweight4 <span class="token1">=</span> $flyweightFactory<span class="token1">-</span><span class="token1">></span><span class="token4">getFlyweight</span><span class="token3">(</span><span class="token2">"state B"</span><span class="token3">)</span><span class="token3">;</span> $unflyweight <span class="token1">=</span> <span class="token5">new</span> <span class="token4">UnsharedConcreteFlyweight</span><span class="token3">(</span><span class="token2">"C"</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//非共享可以使是其他任何类对象</span> $flyweight1<span class="token1">-</span><span class="token1">></span><span class="token4">operation</span><span class="token3">(</span>$unflyweight<span class="token3">)</span><span class="token3">;</span> $flyweight2<span class="token1">-</span><span class="token1">></span><span class="token4">operation</span><span class="token3">(</span>$unflyweight<span class="token3">)</span><span class="token3">;</span> $flyweight3<span class="token1">-</span><span class="token1">></span><span class="token4">operation</span><span class="token3">(</span>$unflyweight<span class="token3">)</span><span class="token3">;</span> $flyweight4<span class="token1">-</span><span class="token1">></span><span class="token4">operation</span><span class="token3">(</span>$unflyweight<span class="token3">)</span><span class="token3">;</span> ``` ``` **例子2:** ``` <pre class="calibre10">``` <span class="token">//所有具体享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态。</span> abstract class <span class="token4">Flyweight</span> <span class="token3">{</span> <span class="token">//内部状态 string</span> public $intrinsic<span class="token3">;</span> <span class="token">//外部状态 String</span> protected $extrinsic<span class="token3">;</span> <span class="token">//这里要求享元角色必须接受外部状态</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>$extrinsic<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>extrinsic <span class="token1">=</span> $extrinsic<span class="token3">;</span> <span class="token3">}</span> <span class="token">/** * 定义业务操作 * @param int $extrinsic 外部状态 */</span> public abstract <span class="token5">function</span> <span class="token4">operate</span><span class="token3">(</span>$extrinsic<span class="token3">)</span><span class="token3">;</span> <span class="token">/** * * @return String 获取外部状态 */</span> public <span class="token5">function</span> <span class="token4">getIntrinsic</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> intrinsic<span class="token3">;</span> <span class="token3">}</span> <span class="token">/** * * @param String $intrinsic 设置外部状态 */</span> public <span class="token5">function</span> <span class="token4">setIntrinsic</span><span class="token3">(</span>$intrinsic<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>intrinsic <span class="token1">=</span> $intrinsic<span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//继承Flyweight超类或实现Flyweight接口,并为其内部状态增加存储空间</span> class <span class="token4">ConcreteFlyweight</span> extends <span class="token4">Flyweight</span> <span class="token3">{</span> <span class="token">/** * 实例化时就接受外部状态 * @param String $extrinsic [description] */</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>$extrinsic<span class="token3">)</span> <span class="token3">{</span> parent<span class="token3">:</span><span class="token3">:</span><span class="token4">__construct</span><span class="token3">(</span>$extrinsic<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">/** * 根据外部状态进行逻辑处理 * @param int $extrinsic [description] * @return [type] [description] */</span> public <span class="token5">function</span> <span class="token4">operate</span><span class="token3">(</span>$extrinsic<span class="token3">)</span> <span class="token3">{</span> echo <span class="token2">"具体Flyweight:"</span><span class="token3">.</span>$extrinsic<span class="token3">.</span><span class="token2">"<br>"</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//指那些不需要共享的Flyweight子类。</span> class <span class="token4">UnsharedConcreteFlyweight</span> extends <span class="token4">Flyweight</span> <span class="token3">{</span> <span class="token">/** * * @param String $extrinsic [description] */</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>$extrinsic<span class="token3">)</span> <span class="token3">{</span> parent<span class="token3">:</span><span class="token3">:</span><span class="token4">__construct</span><span class="token3">(</span>$extrinsic<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">/** * * @param int $extrinsic [description] * @return [type] [description] */</span> public <span class="token5">function</span> <span class="token4">operate</span><span class="token3">(</span>$extrinsic<span class="token3">)</span> <span class="token3">{</span> echo <span class="token2">"不共享的具体Flyweight:"</span> <span class="token3">.</span> $extrinsic <span class="token3">.</span><span class="token2">"<br>"</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//享元工厂,用来创建并管理Flyweight对象</span> class <span class="token4">FlyweightFactory</span> <span class="token3">{</span> <span class="token">//定义一个池容器</span> private $pool <span class="token1">=</span><span class="token4">array</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//享元工厂</span> <span class="token">/** * / * @param String $extrinsic [description] * @return Flyweight [description] */</span> public <span class="token5">function</span> <span class="token4">getFlyweight</span><span class="token3">(</span>String $extrinsic<span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span><span class="token3">(</span><span class="token1">!</span><span class="token4">isset</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>pool<span class="token3">[</span>$extrinsic<span class="token3">]</span><span class="token3">)</span><span class="token3">)</span> <span class="token3">{</span> <span class="token">//池中没有该对象</span> <span class="token">//根据外部状态创建享元对象并放入池中</span> $this<span class="token1">-</span><span class="token1">></span>pool<span class="token3">[</span>$extrinsic<span class="token3">]</span> <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ConcreteFlyweight</span><span class="token3">(</span>$extrinsic<span class="token3">)</span><span class="token3">;</span> echo <span class="token2">"创建 "</span> <span class="token3">.</span> $extrinsic <span class="token3">.</span> <span class="token2">" 并从池中取出----><br>"</span><span class="token3">;</span> <span class="token3">}</span><span class="token5">else</span><span class="token3">{</span> echo <span class="token2">"已有 "</span> <span class="token3">.</span> $extrinsic <span class="token3">.</span> <span class="token2">" 直接从池中取----><br>"</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>pool<span class="token3">[</span>$extrinsic<span class="token3">]</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> class <span class="token4">Client</span> <span class="token3">{</span> public static <span class="token5">function</span> <span class="token4">main</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> $extrinsic <span class="token1">=</span> <span class="token6">22</span><span class="token3">;</span> $FlyweightFactory<span class="token1">=</span> <span class="token5">new</span> <span class="token4">FlyweightFactory</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $flyweightX <span class="token1">=</span> $FlyweightFactory<span class="token1">-</span><span class="token1">></span><span class="token4">getFlyweight</span><span class="token3">(</span><span class="token2">"X"</span><span class="token3">)</span><span class="token3">;</span> $flyweightX<span class="token1">-</span><span class="token1">></span><span class="token4">operate</span><span class="token3">(</span><span class="token1">++</span>$extrinsic<span class="token3">)</span><span class="token3">;</span> $flyweightY <span class="token1">=</span> $FlyweightFactory<span class="token1">-</span><span class="token1">></span><span class="token4">getFlyweight</span><span class="token3">(</span><span class="token2">"Y"</span><span class="token3">)</span><span class="token3">;</span> $flyweightY<span class="token1">-</span><span class="token1">></span><span class="token4">operate</span><span class="token3">(</span><span class="token1">++</span>$extrinsic<span class="token3">)</span><span class="token3">;</span> $flyweightZ <span class="token1">=</span> $FlyweightFactory<span class="token1">-</span><span class="token1">></span><span class="token4">getFlyweight</span><span class="token3">(</span><span class="token2">"Z"</span><span class="token3">)</span><span class="token3">;</span> $flyweightZ<span class="token1">-</span><span class="token1">></span><span class="token4">operate</span><span class="token3">(</span><span class="token1">++</span>$extrinsic<span class="token3">)</span><span class="token3">;</span> $flyweightReX <span class="token1">=</span> $FlyweightFactory<span class="token1">-</span><span class="token1">></span><span class="token4">getFlyweight</span><span class="token3">(</span><span class="token2">"X"</span><span class="token3">)</span><span class="token3">;</span> $flyweightReX<span class="token1">-</span><span class="token1">></span><span class="token4">operate</span><span class="token3">(</span><span class="token1">++</span>$extrinsic<span class="token3">)</span><span class="token3">;</span> <span class="token">//不共享的直接new 而不经过享元工厂</span> $unsharedFlyweight <span class="token1">=</span> <span class="token5">new</span> <span class="token4">UnsharedConcreteFlyweight</span><span class="token3">(</span><span class="token2">"X"</span><span class="token3">)</span><span class="token3">;</span> $unsharedFlyweight<span class="token1">-</span><span class="token1">></span><span class="token4">operate</span><span class="token3">(</span><span class="token1">++</span>$extrinsic<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> Client<span class="token3">:</span><span class="token3">:</span><span class="token4">main</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> 结果: 创建 X 并从池中取出<span class="token1">--</span><span class="token1">--</span><span class="token1">></span> 具体Flyweight<span class="token3">:</span><span class="token6">23</span> 创建 Y 并从池中取出<span class="token1">--</span><span class="token1">--</span><span class="token1">></span> 具体Flyweight<span class="token3">:</span><span class="token6">24</span> 创建 Z 并从池中取出<span class="token1">--</span><span class="token1">--</span><span class="token1">></span> 具体Flyweight<span class="token3">:</span><span class="token6">25</span> 已有 X 直接从池中取<span class="token1">--</span><span class="token1">--</span><span class="token1">></span> 具体Flyweight<span class="token3">:</span><span class="token6">26</span> 不共享的具体Flyweight<span class="token3">:</span><span class="token6">27</span> ``` ``` ## **享元模式的扩展** (1)**单纯享元模式**,这种享元模式中的所有的具体享元类都是可以共享的,不存在非共享的具体享元类,其结构图如图所示。 ![](https://img.kancloud.cn/41/0b/410b32f9c30cd180db6a8fdf10664900_513x496.png) (2) **复合享元模式**,这种享元模式中的有些享元对象是由一些单纯享元对象组合而成的,它们就是复合享元对象。虽然复合享元对象本身不能共享,但它们可以分解成单纯享元对象再被共享,其结构图如图所示 ![](https://img.kancloud.cn/f4/4c/f44c3008047178b4973127251e6f1c08_769x518.png)