享元模式
运用共享技术有效地支持大量细粒度的对象。
Flyweight Pattern
Use sharing to support large numbers of fine-grained objects efficiently.
应用场景:
一个类中的成员变量表明了该类所创建的对象所具有的属性。在某些程序中,我们可能用一个类创建了若干个对象,但是我们发现这些对象的一个共同特点是它们的一部分属性的取值必须完全相同。这个时候就可以采用享元模式,来实现部分相同属性的共享。它是一种对象结构型模式。
主要角色:
- 享元接口(Flyweight)
- 具体享元(ConcreteFlyweght)
- 享元工厂(FlyweightFactory)
- 客户端
UML类图:
php实现及用例:
1 <?php 2 interface Flyweight{ 3 public function getHeight(); 4 public function getWidth(); 5 public function getLength(); 6 public function printMess($mess); 7 } 8 9 class ConcreteFlyweight implements Flyweight{ 10 private $height; 11 private $width; 12 private $length; 13 public function __construct($height, $width, $length){ 14 $this->height = $height; 15 $this->width = $width; 16 $this->length = $length; 17 } 18 public function getHeight(){ 19 return $this->height; 20 } 21 public function getWidth(){ 22 return $this->width; 23 } 24 public function getLength(){ 25 return $this->length; 26 } 27 public function printMess($mess){ 28 echo "$mess 宽度=$this->width 高度=$this->height 长度=$this->length\n"; 29 } 30 } 31 class FlyweightFactory{ 32 private $hashMap = array(); //用数组表示hashmap数据接口 key->value 33 private static $factory; 34 private function __construct(){} 35 public static function getFactory(){ 36 if(!self::$factory instanceOf FlyweightFactory){ 37 self::$factory = new self(); 38 } 39 return self::$factory; 40 } 41 public function getFlyweight($key){ 42 if(isset($this->hashMap[$key])) 43 return $this->hashMap[$key]; 44 else{ 45 list($height,$width,$length) = explode("#",$key); 46 $fw = new ConcreteFlyweight((float)$height, (float)$width, (float)$length); 47 $this->hashMap[$key] = $fw; 48 return $fw; 49 } 50 } 51 }
接下来,运用享元模式实现Car类,并实例化多个对象
1 class Car{ 2 private $flyweight; 3 private $name; 4 private $color; 5 private $power; 6 public function __construct($flyweight, $name, $color, $power){ 7 $this->flyweight = $flyweight; 8 $this->name = $name; 9 $this->color = $color; 10 $this->power = $power; 11 } 12 public function printValue(){ 13 echo '名称='.$this->name.' 颜色='.$this->color.' 功率='.$this->power.' 宽度='.$this->flyweight->getWidth(). 14 ' 高度='.$this->flyweight->getHeight().' 长度='.$this->flyweight->getLength()."\n"; 15 } 16 } 17 18 19 function main(){ 20 $factory = FlyweightFactory::getFactory(); 21 $height = 1.47; $width = 1.82; $length = 5.12; 22 $key = $height.'#'.$width.'#'.$length; 23 $flyweight = $factory->getFlyweight($key); 24 $audiA6_One = new Car($flyweight, '奥迪A6', '黑色', 128); 25 $audiA6_Two = new Car($flyweight, '奥迪A6', '灰色', 160); 26 $audiA6_One->printValue(); 27 $audiA6_Two->printValue(); 28 29 $height = 1.45; $width = 1.77; $length = 4.63; 30 $key = $height.'#'.$width.'#'.$length; 31 $flyweight = $factory->getFlyweight($key); 32 $audiA6_Three = new Car($flyweight, '奥迪A4', '蓝色', 126); 33 $audiA6_Four = new Car($flyweight, '奥迪A4', '红色', 138); 34 $audiA6_Three->printValue(); 35 $audiA6_Four->printValue(); 36 } 37 main();
模式分析:
享元模式是一个考虑系统性能的设计模式,通过使用享元模式可以节约内存空间,提高系统的性能。
享元模式的核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。
享元模式以共享的方式高效地支持大量的细粒度对象,享元对象能做到共享的关键是区分内部状态(Internal State)和外部状态(External State)。
内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,因此内部状态可以共享。
外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。
优点:
享元模式的优点在于它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。
享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。
缺点:
享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。
单纯享元模式和复合享元模式:
单纯享元模式:在单纯享元模式中,所有的享元对象都是可以共享的,即所有抽象享元类的子类都可共享,不存在非共享具体享元类
复合享元模式:将一些单纯享元使用组合模式加以组合,可以形成复合享元对象,这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者则可以共享。