PHP 设计模式系列 —— 对象池模式(Object Pool)

1、模式定义

对象池(也称为资源池)被用来管理对象缓存。对象池是一组已经初始化过且可以直接使用的对象集合,用户在使用对象时可以从对象池中获取对象,对其进行操作处理,并在不需要时归还给对象池而非销毁它。

若对象初始化、实例化的代价高,且需要经常实例化,但每次实例化的数量较少的情况下,使用对象池可以获得显著的性能提升。常见的使用对象池模式的技术包括线程池、数据库连接池、任务队列池、图片资源对象池等。

当然,如果要实例化的对象较小,不需要多少资源开销,就没有必要使用对象池模式了,这非但不会提升性能,反而浪费内存空间,甚至降低性能。

2、UML类图

对象池模式类图

3、示例代码

Pool.php

<?php

namespace DesignPatterns\Creational\Pool;

class Pool
{

    private $instances = array();
    private $class;

    public function __construct($class)
    {
        $this->class = $class;
    }

    public function get()
    {
        if (count($this->instances) > 0) {
            return array_pop($this->instances);
        }

        return new $this->class();
    }

    public function dispose($instance)
    {
        $this->instances[] = $instance;
    }
}

Processor.php

<?php

namespace DesignPatterns\Creational\Pool;

class Processor
{

    private $pool;
    private $processing = 0;
    private $maxProcesses = 3;
    private $waitingQueue = [];

    public function __construct(Pool $pool)
    {
        $this->pool = $pool;
    }

    public function process($image)
    {
        if ($this->processing++ < $this->maxProcesses) {
            $this->createWorker($image);
        } else {
            $this->pushToWaitingQueue($image);
        }
    }

    private function createWorker($image)
    {
        $worker = $this->pool->get();
        $worker->run($image, array($this, 'processDone'));
    }

    public function processDone($worker)
    {
        $this->processing--;
        $this->pool->dispose($worker);

        if (count($this->waitingQueue) > 0) {
            $this->createWorker($this->popFromWaitingQueue());
        }
    }

    private function pushToWaitingQueue($image)
    {
        $this->waitingQueue[] = $image;
    }

    private function popFromWaitingQueue()
    {
        return array_pop($this->waitingQueue);
    }
}

Worker.php

<?php

namespace DesignPatterns\Creational\Pool;

class Worker
{

    public function __construct()
    {
        // let's say that constuctor does really expensive work...
        // for example creates "thread"
    }

    public function run($image, array $callback)
    {
        // do something with $image...
        // and when it's done, execute callback
        call_user_func($callback, $this);
    }
}

4、测试代码

Tests/PoolTest.php

<?php

namespace DesignPatterns\Creational\Pool\Tests;

use DesignPatterns\Creational\Pool\Pool;

class PoolTest extends \PHPUnit_Framework_TestCase
{
    public function testPool()
    {
        $pool = new Pool('DesignPatterns\Creational\Pool\Tests\TestWorker');
        $worker = $pool->get();

        $this->assertEquals(1, $worker->id);

        $worker->id = 5;
        $pool->dispose($worker);

        $this->assertEquals(5, $pool->get()->id);
        $this->assertEquals(1, $pool->get()->id);
    }
}

Tests/TestWorker.php

<?php

namespace DesignPatterns\Creational\Pool\Tests;

class TestWorker
{
    public $id = 1;
}

学院君 has written 880 articles

Laravel学院院长,终身学习者

积分:99492 等级:P12 职业:码农 城市:杭州

7 条回复

  1. 张小张 张小张 says:
    写错了,少加了一行 if (count($this->waitingQueue) > 0) { $image = $this->popFromWaitingQueue(); $worker->run($image, array($this, 'processDone')); } else { $this->processing--; $this->pool->dispose($worker); }
  2. 张小张 张小张 says:
    @ 粟浩翔 不会啊, if (count($this->waitingQueue) > 0) { $this->createWorker($this->popFromWaitingQueue()); } 改成 if (count($this->waitingQueue) > 0) { $worker->run($image, array($this, 'processDone')); } else { $this->processing--; $this->pool->dispose($worker); } 这样应该也是可以的
  3. 李林 李林 says:
    这些例子的代码对于对象池模式表达得不是很好
  4. 伊凡 伊凡 says:
    这章看的不是很懂。能说下这三个类有什么联系吗?
  5. 周铭 周铭 says:
    @ 工控资料窝 如果不先回收,把processing这个属性减一的话,一旦等待队列中有任务,就回收不了,得一直等到队列清空后才能回收。而processing大于maxProcess的话,会把任务推送到等待队列,这样任务一多,旧的进程没有回收,一直执行出栈队列,而队列又超过最大process,一直入栈队列。取现循环下去,任务就没法执行了。这只是我的想法而已,不知道是否正确
  6. 林非 林非 says:
    Processer->processDone()方法中,如果先判断是否有等待的任务,有的话就直接用这个 $worker 去继续处理任务,没有就回收,是否会好一些?

登录后才能进行评论,立即登录?