浅析PHP中Collection 类的设计

  用.net开发已经很多年了,最近接触到php,发现php也很好玩。不过发现它里面没有集合Collection类,只有数组,并且数组很强。这里我用数组来包装成一个集合Collection,代码如下:

  

复制代码 代码如下:

  class Collection{

  private $_members=array();

  public  function addItem($obj,$key=null)

  {

  if($key)

  {

  if(isset($this->_members[$key]))

  {

  throw  new exception("Key \"$key\" already in use!");

  }

  else

  {

  $this->_members[$key]=$obj;

  }

  }

  else

  {

  $this->_members[]=$obj;

  }

  }

  public function removeItem($key)

  {

  if(isset($this->_members[$key]))

  {

  unset($this->_members[$key]);

  }

  else

  {

  throw new exception("Invalid Key \"$key\"!");

  }

  }

  public function getItem($key)

  {

  if(isset($this->_members[$key]))

  {

  return $this->_members[$key];

  }

  else

  {

  throw new  exception("Invalid Key \"$key\"!");

  }

  }

  public function Keys()

  {

  return array_keys($this->_members);

  }

  public function legth()

  {

  return sizeof($this->_members);

  }

  public function exists($key)

  {

  return (isset($this->_members[$key]));

  }

  }

  现在我们来测试一下这个集合是否好用。

  我们首先建立一个集合元素类Course:

  

复制代码 代码如下:

  class  Course

  {

  private $_id;

  private $_courseCode;

  private $_name;

  public function __construct($id,$courseCode,$name)

  {

  $this->_id=$id;

  $this->_courseCode=$courseCode;

  $this->_name=$name;

  }

  public function getName()

  {

  return $this->_name;

  }

  public function getID()

  {

  return $this->_id;

  }

  public function getCourseCode()

  {

  return $this->_courseCode;

  }

  public function __toString()

  {

  return $this->_name;

  }

  }

  测试代码如下:

  $courses=new Collection();

  $courses->addItem(new Course(1, "001", "语文"),1);

  $courses->addItem(new Course(2, "002", "数学"),2);

  $obj=$courses->getItem(1);

  print $obj;

  我想这个集合类应该可以满足我们平日开发的需求了吧。

  可是我们现在。net里面有个对象延迟加载,举个例子来说吧,假如现在有Student这个对象,它应该有很多Course,但是我们希望在访问Course之前Course是不会加载的。也就是说在实例化Student的时候Course个数为0,当我们需要Course的时候它才真正从数据库读取相应数据。就是需要我们把Collection做成惰性实例化。

  修改后的Collection代码如下:

  

复制代码 代码如下:

  class Collection {

  private $_members = array();    //collection members

  private $_onload;               //holder for callback function

  private $_isLoaded = false;     //flag that indicates whether the callback

  //has been invoked

  public function addItem($obj, $key = null) {

  $this->_checkCallback();      //_checkCallback is defined a little later

  if($key) {

  if(isset($this->_members[$key])) {

  throw new KeyInUseException("Key \"$key\" already in use!");

  } else {

  $this->_members[$key] = $obj;

  }

  } else {

  $this->_members[] = $obj;

  }

  }

  public function removeItem($key) {

  $this->_checkCallback();

  if(isset($this->_members[$key])) {

  unset($this->_members[$key]);

  } else {

  throw new KeyInvalidException("Invalid key \"$key\"!");

  }

  }

  public function getItem($key) {

  $this->_checkCallback();

  if(isset($this->_members[$key])) {

  return $this->_members[$key];

  } else {

  throw new KeyInvalidException("Invalid key \"$key\"!");

  }

  }

  public function keys() {

  $this->_checkCallback();

  return array_keys($this->_members);

  }

  public function length() {

  $this->_checkCallback();

  return sizeof($this->_members);

  }

  public function exists($key) {

  $this->_checkCallback();

  return (isset($this->_members[$key]));

  }

  /**

  * Use this method to define a function to be

  * invoked prior to accessing the collection.

  * The function should take a collection as a

  * its sole parameter.

  */

  public function setLoadCallback($functionName, $objOrClass = null) {

  if($objOrClass) {

  $callback = array($objOrClass, $functionName);

  } else {

  $callback = $functionName;

  }

  //make sure the function/method is valid

  if(!is_callable($callback, false, $callableName)) {

  throw new Exception("$callableName is not callable " .

  "as a parameter to onload");

  return false;

  }

  $this->_onload = $callback;

  }

  /**

  * Check to see if a callback has been defined and if so,

  * whether or not it has already been called.  If not,

  * invoke the callback function.

  */

  private function _checkCallback() {

  if(isset($this->_onload) && !$this->_isLoaded) {

  $this->_isLoaded = true;

  call_user_func($this->_onload, $this);

  }

  }

  }

  所需的Student如下:

  

复制代码 代码如下:

  class CourseCollection extends Collection {

  public function addItem(Course $obj,$key=null) {

  parent::addItem($obj,$key);

  }

  }

  class Student{

  private $_id;

  private $_name;

  public $course;

  public  function __construct($id,$name)

  {

  $this->_id=$id;

  $this->_name=$name;

  $this->course=new CourseCollection();

  $this->course->setLoadCallback('loadCourses',$this);

  }

  public function getName()

  {

  return $this->_name;

  }

  public function getID()

  {

  return $this->_id;

  }

  public function __toString()

  {

  return $this->_name;

  }

  public function loadCourses(Collection $col)

  {

  $col->addItem(new Course(1, "001", "语文"),1);

  $col->addItem(new Course(2, "002", "数学"),2);

  }

  }

  调用代码如下:

  $student=new Student(1, "majiang");

  print $student->getName();

  print $student->course->getItem(1);