php设计模式 FlyWeight (享元模式) |
享元模式英文称为“Flyweight Pattern”,我非常感谢将Flyweight Pattern翻译成享元模式的那位强人,因为这个词将这个模式使用的方式明白得表示了出来;如果翻译成为羽量级模式或者蝇量级模式等等,虽然可以含蓄的表现出使用此模式达到的目的,但是还是没有抓住此模式的关键 。 享元模式的定义为:采用一个共享来避免大量拥有相同内容对象的开销 。这种开销中最常见、直观的就是内存的损耗 。享元模式以共享的方式高效的支持大量的细粒度对象 。 在名字和定义中都体现出了共享这一个核心概念,那么怎么来实现共享呢?要知道每个事物都是不同的,但是又有一定的共性,如果只有完全相同的事物才能共享,那么享元模式可以说就是不可行的;因此我们应该尽量将事物的共性共享,而又保留它的个性 。为了做到这点,享元模式中区分了内蕴状态和外蕴状态 。内蕴状态就是共性,外蕴状态就是个性了 。 注:共享的对象必须是不可变的,不然一变则全变(如果有这种需求除外) 。 内蕴状态存储在享元内部,不会随环境的改变而有所不同,是可以共享的;外蕴状态是不可以共享的,它随环境的改变而改变的,因此外蕴状态是由客户端来保持(因为环境的变化是由客户端引起的) 。在每个具体的环境下,客户端将外蕴状态传递给享元,从而创建不同的对象出来 。 先看看下面程序,大概了解下享元模式 。 复制代码 代码如下: <?php /** * 享元模式 * * 运用享元技术有效的支持大量细粒度的对象 */ class CD { private $_title = null; private $_artist = null; public function setTitle($title) { $this->_title = $title; } public function getTitle() { return $this->_title; } public function setArtist($artist) { $this->_artist = $artist; } public function getArtist($artist) { return $this->_artist; } } class Artist { private $_name; public function __construct($name) { echo "construct ".$name."<br/>"; $this->_name = $name; } public function getName() { return $this->_name; } } class ArtistFactory { private $_artists = array(); public function getArtist($name) { if(isset($this->_artists[$name])) { return $this->_artists[$name]; } else { $objArtist = new Artist($name); $this->_artists[$name] = $objArtist; return $objArtist; } } } $objArtistFactory = new ArtistFactory(); $objCD1 = new CD(); $objCD1->setTitle("title1"); $objCD1->setArtist($objArtistFactory->getArtist(artist1)); $objCD2 = new CD(); $objCD2->setTitle("title2"); $objCD2->setArtist($objArtistFactory->getArtist(artist2)); $objCD3 = new CD(); $objCD3->setTitle("title3"); $objCD3->setArtist($objArtistFactory->getArtist(artist1)); 享元模式的精要有三点:
面向对象虽然很好地解决了抽象性的问题,但是对于一个实际运行的软件系统,我们还需要考虑面向对象的代价问题,享元模式解决的就是面向对象的代价问题 。享元模式采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力 。 享元模式在一般的项目开发中并不常用,而是常常应用于系统底层的开发,以便解决系统的性能问题 。Java和.Net中的String类型就是使用了享元模式 。如果在Java或者.NET中已经创建了一个字符串对象s1,那么下次再创建相同的字符串s2的时候,系统只是把s2的引用指向s1所引用的具体对象,这就实现了相同字符串在内存中的共享 。如果每次执行s1=“abc”操作的时候,都创建一个新的字符串对象的话,那么内存的开销会很大 。 |