1

I am writing a php app with subclasses and since I want to have multiple ways to create an object I am doing different factory methods instead of multiple constructors.

I have a User with factory methods

User::from_id
User::from_netid

I have several subclasses of User. I was previously calling the parent superconstructor, but when I switched to the factory method that constructor didn't exist.

I have Student, a subclass of User. To get it to work, I had to duplicate almost all of my superclass factory code in User::from_id to load_by_id, since in this situation the instance already existed:

// In Student.php - Student extends User
public static function from_id_and_course($id, $course){
    $instance = new self();
    $instance->load_by_id($id);
    $instance->course = $course;
    ...
}

I want to call the superclass factory method from the subclass as a starting point, and then continue to add the other fields. Something like this...

$instance = User::from_id($id);

or

$instance = Student::from_id($id);

but in these cases it gives me a User object, and I need a Student object. The only way I could accomplish this is by doing $instance = new self().

How can I call the superclass factory method from the subclass as a starting point to create a new subclass factory method?

jkeesh
  • 3,289
  • 3
  • 29
  • 42
  • Just FYI you are not implementing a true factory pattern here. You are meant to have another class that acts as the factory so that any class that wants to use the student / users / whatever subclasses is loosely coupled to those not tightly coupled by calling specific methods of the user classes. – Simon H Dec 21 '11 at 01:18
  • @ZeSimon - this seems to be the [Factory Method](http://en.wikipedia.org/wiki/Factory_method_pattern) pattern rather than the full Factory pattern – HorusKol Dec 21 '11 at 01:20
  • Agreed, however I think in this case he would benefit from the full factory pattern. – Simon H Dec 21 '11 at 01:22
  • @HorusKol: I think you're confusing the Builder pattern (what you call the full Factory pattern) and the Factory pattern. [This is an example of the factory pattern.](http://stackoverflow.com/a/4719976/492901) – netcoder Dec 21 '11 at 02:03
  • Have you tried using `parent::from_id($id);` – Indranil Dec 21 '11 at 01:17
  • @netcoder - yeah, probably... brain seems to have shutdown for the holidays – HorusKol Dec 21 '11 at 04:23

2 Answers2

5

Your problem is this:

$instance = new self();

self refers to the class where the method is defined, not the caller:

  • When Student::from_id() is called, if it doesn't exist, it falls back to User::from_id().
  • In User::from_id(), self refers to User, not Student.

You'd have to use late-static bindings:

$instance = new static();

However, like I always do, I'd highly recommend against it. You're better off using the object scope than the static scope. It's easier to extend, to fake or mock and incidentally, to test.

There's nothing wrong with:

$user = new User;
$user->from_id($id);

$student = new Student;
$student->from_id($id);

...it's actually better.

netcoder
  • 66,435
  • 19
  • 125
  • 142
1

If you're using PHP 5.3 or higher, you could use the Late Static Bindings that are now available:

class User
{
  public static function from_id($id)
  {
    // factory
    $object = new static();

    // setup

    // return
    return $object;
  }
}

class Student extends User { }

$student = Student::from_id($id); // $student should be of class Student

Note - you're probably better off setting up a whole factory class for mocking/testing/sanity...

HorusKol
  • 8,375
  • 10
  • 51
  • 92