享元模式 (Flyweight Pattern): 池技术的重要实现方式, 运用共享技术有效的支持大量的细粒度对象,用于减少创建对象的数量,以减少内存占用和提高性能。 ## (一)为什么需要享元模式 1,在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。 2,系统有大量相似对象。 3,需要缓冲池的场景。 ## (二)享元模式UML图 ![](https://box.kancloud.cn/109fa428aea41b06f8123f3cf146e8b5_1145x584.png) ## (三)简单实例 这里引用《设计模式之禅》的例子,就是如果有一个报考信息系统,每个考生进来就实例化一个对象,传入他要报考的考点。由于考生分为30个考点,所以其实我们没有必要不停的实例化对象并释放,我们完全可以做一个缓冲池,若缓冲池中没有与这个考生相同考点的对象,则实例化一个,使用完不需要释放。下一个相同考点的考生进来后,只需复用这个对象。 <?php //抽象享元对象 abstract class Flyweight{ //考点 public $address; //享元角色必修设置考点 public function __construct($address){ $this->address = $address; } } //具体享元角色 考生类 class ConcreteFlyweight extends Flyweight{ //报考动作 public function register(){ echo "我的报考点是:{$this->address}".PHP_EOL; } //退出 public function quit(){ unset($this); } } //享元工厂 缓冲池 class FlyweightFactor{ static private $students = array(); static public function getStudent($address){ $students =self::$students; //判断该键值是否存在 if(array_key_exists($address,$students)){ echo "缓冲池有考点为{$address},从池中直接取".PHP_EOL; }else{ echo "缓冲池没有,创建了考点为{$address}的对象并放到池中".PHP_EOL; self::$students[$address] = new ConcreteFlyweight($address); } return self::$students[$address]; } } //实例化学生对象 $student_1 = FlyweightFactor::getStudent('广州'); //报考 $student_1 ->register(); // 退出 $student_1->quit(); //第二个学生进来 $student_2 = FlyweightFactor::getStudent('东莞'); //报考 $student_2 ->register(); // 退出 $student_2->quit(); //第三个学生进来 $student_3 = FlyweightFactor::getStudent('广州'); //报考 $student_3 ->register(); // 退出 $student_3->quit(); 这里我们可以看到,当第三个学生进来时,由于和第一个学生的报考地点一致,所以只需从缓冲池取出。 ps:虽然ConcreteFlyweight的quit方法unset了$this,但是由于在FlyweightFactor中的students还存放着这个对象,所以unset只释放了变量$student_1,并没有完全删除这个对象,这就是student_3进来还能从缓冲池取得对象的原因 享元模式在PHP中可能比较少遇,但在Java中常常有这种情况。就是代码产生了大量的对象,虽然使用完有释放,但是由于垃圾回收需要一定时间,导致内存被耗尽。PHP经常应用web编程,脚本执行时间很短(30秒)。所以很少遇见这种情况,甚至我们使用完变量,连unset()函数都不用调用,就等着脚步执行结束后,自动释放。但是如果你试过在cli模式下运行PHP脚本,做一些socket通信,发送邮件等长耗时或是多连接任务时,就难免会遇到这种情况。