5

I came up with a solution (in PHP) to a problem and am wondering if it is a named Design Pattern, and if it is good practice.

I have a collection class and an item class. The collection class is the only one allowed to set the item class's protected data, besides the class itself.

I did that by giving the item class a method that takes the collection object as an argument. The item object then sets its data with the collection objects.

That way, we can say:

$item_coll = new collection();
$item_coll->load($criterion1, $criterion2);

Because the collection implements IteratorAggregate, we can also do this:

foreach($item_coll as $item) {
  echo 'Item name is ' . $item->getName();
}

Even after a collection is loaded, you can get a sub collection using the getBy($property,$value) method:

$sub_collection = $item_coll->getBy('color','green'); // new collection object with green items

And now I have a collection of item objects with all their data set. Here are the classes (with impertinent methods/properties not shown).

class item {
  protected $id; // id from database table
  protected $prop1; // properties also from database
  protected $prop2;

  public function setData(collection $coll, $id) {
    $data = $coll->getData($id);
    foreach($data as $key=>$value) {
      $this->$key = $value
    }
    return $this;
  }
  public function getId() {
    return $this->id;
  }
}

class collection implements IteratorAggregate { // interface for foreach access to $collection
    protected $data = array();
    protected $collection = array(); // contains `item` objects
    public function getData($id) {
      if(array_key_exists($id,$this->data) ) {
        return $this->data[$id];
      } else return array();
    }
    public function load(someClass $obj1, someClass2 $obj2) { // arguments not relevant here, they determine the contents of the collection
      // based on arguments, data is loaded from database into $this->data;
      foreach($rows as $row) { // database data
        $this->data[$id] = $row;
        $obj = $this->collection[$id] = new item();
        $obj->setData($this,$id); // collection object passes itself into item object
      }
    }
    public function html() {//
      foreach($this as $obj) {
        // loop through collection to create an html table for display
      }
    }
    public function addItem(item $item) { // add already-existing items to collection
      if(!array_key_exists($id,$this->collection) ) {
        $this->collection[$item->getId()] = $item;
      }
    }
    public function getIterator() { // for IteratorAggregate interface
      return new ArrayIterator($this->collection);
    }
    public function getBy($property,$value) {
      $collection = new static(); // new collection created
      // loop through $this object and fill $collection with matching item objects
      return $collection;
    }
    public function getById($id) { // get a single item object by its id
      if(array_key_exists($id,$this->collection) ) {
        return $this->collection[$id];
      }
    }
}
  • @StefanHanke You are saying that I might have a problem if I want a class other than collection to be able to call the setData() method of item? My purpose was to limit it to the collection class, but it's possible that in the future I might want to allow another class. So do you think this is a bad idea? Should I not worry about who is setting the object's data, and perhaps have the object validate whatever data is being set? Or have some kind of validator class? My purpose was to establish a trusted "partner class" of sorts. – Buttle Butkus Feb 25 '15 at 8:23
  • Ah... I deleted my comment - don't know what made me post that... sorry. Actually I never saw anything like this. Can you please elaborate a bit on how the collection is actually used after being loaded? How can an item be retrieved? Is the $id visible to the "outside"? – Stefan Hanke Feb 25 '15 at 18:49
  • @StefanHanke As per my recent edit, you can loop through the collection object to access each member. You can also retrieve a sub-collection, or a single item by id. The properties are protected. I used to use a get($property) method in a lot of classes but recently have been starting to change over to writing specific methods like getId() to limit access. Currently, $id is visible through those methods. – Buttle Butkus Feb 25 '15 at 19:53
  • Do you intend to use it for immutable objects creation or you plan to add (collection) methods for modifying items' properties (why would that make a difference to direct modification then)? – shudder Jun 3 '15 at 9:10
  • I have collection methods that can return a part of the collection as a new collection object, or can get aggregate information about the collection, like collection size (of course, an array can do that easily), or number of objects with this or that property value in the collection. I don't really want every class to be able to alter the objects' values. But maybe I should be worrying about that? – Buttle Butkus Jun 3 '15 at 22:32
1

I think you are trying to use dependency injection but the code is not easily readable. You could have used three classes instead of two:

  1. Collection: to hold the items; in fact you could just use a simple array
  2. Item
  3. ItemDataManager: to load and cache the data from the DB in an efficient manner.

With this approach your code would be:

$idm = new ItemDataManager();
$idm->load(criterion1, criterion2);

And then you could pass $idm as a parameter to $item->setData. This way you get a clean separation of duties between the collection management and the data management..

1

The pattern you are referring to is called Dependency Injection

Basically, in most cases, the application calls the library/object for the information it needs, but in some cases, the object needs to first get configured what should it serve the application with. But if a generic object assumes or gets it from a hard-coded location (say config file etc.) then it is not re-usable in context of other application. Hence, the library object is dependent back on Application to provide this information. Creating API in a library/object where by Application feeds the information back is called Dependency Injection.

There are three main ways to exercise dependency injection:

  1. Constructor method: Where parameters are supplied at the time of calling the constructor.
  2. Setter method: where standard get/set method does the job.
  3. Interface method: where a dedicated method is used by application to pass the information.

The -load() method is essentially the example of interface method which is capable of supplying multiple values and complex objects in one go.

Read this article of Martin Fowler which will give you a great perspective on this subject.

0

This looks more closely like the Visitor Pattern, although the control might be reversed. I would actually say that there is no pattern here, just some architectural problems.

You have a collection class which loads data from the database. There is a design pattern that describes this sort of behavior, where you have a data store (doesn't necessarily have to be a database) and an object that represents the "collection" of stored objects: the Repository Pattern.

With very few exceptions, no object outside the class hierarchy of item should be accessing protected data. Instead, you need to expose public methods to set that data, or require those values to be passed in to the object's constructor.

Utilizing the Repository Pattern along with proper encapsulation will render your original question moot.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.