Jump to content

When to use factory classes?


NotionCommotion

Recommended Posts

I have the following classes as shown below...

<?php
//All classes except maybe Animals are NOT abstract
class Animals {
    public $fee, $fi, $fo, $fum;
    public function __construct($settings)
    {
        foreach($settings as $key=>$value) {
            $this->$key=$value;
        }
    }
}
class Mammals extends Animals {
    public $mammalSpecific;
}
class Reptiles extends Animals {
    public $reptileSpecific;
}
class Dogs extends Mammals {}
class Cats extends Mammals {}
class Snakes extends Reptiles {}
class Lizards extends Reptiles {}

$dog=new Dogs(['fee'=>'x', 'fi'=>'x', 'fo'=>'x', 'fum'=>'x', 'mammalSpecific'=>'x']);
$snake=new Snakes(['fee'=>'x', 'fi'=>'x', 'fo'=>'x', 'fum'=>'x', 'reptileSpecific'=>'x']);

All is good, but now I want to get an object based on its ID.  One option is to add a new class as follows:

<?php
class Factories //extends Nothing
{
    private $settings;

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

    public function getMammalByID($id)
    {
        if($classnameAndSpecificAboutThisMammal=$this->getClassnameAndSpecificAboutThisMammalForGivenID($id)) {
            $class=$classnameAndSpecificAboutThisMammal['classname'];
            $obj=new $class(array_merge($settings,['mammalSpecific'=>$classnameAndSpecificAboutThisMammal['specificAboutThisMammal']]));
        }
        return empty($obj)?new invalidObject:$obj;
    }

    public function getReptileByID($id)
    {
        if($classnameAndSpecificAboutThisReptile=$this->getClassnameAndSpecificAboutThisReptileForGivenID($id)) {
            $class=$classnameAndSpecificAboutThisReptile['classname'];
            $obj=new $class(array_merge($settings,['reptileSpecific'=>$classnameAndSpecificAboutThisReptile['specificAboutThisReptile']]));
        }
        return empty($obj)?new invalidObject:$obj;
    }
}

$factory=new Factory(['fee'=>'x', 'fi'=>'x', 'fo'=>'x', 'fum'=>'x']);
$mammal=$factory->getMammalByID(123);
$reptile=$factory->getReptileByID(321);

Or should I just put a getByID() method in both the Mammals and Reptiles classes instead of using a separate Factory class?  But wouldn't this force the Animal constructor (which will likely do more than I have shown) to be executed twice?

 

 

Thanks

 

Link to comment
Share on other sites

Is this another one of your abstract questions that would be better answered if it wasn't abstract? ;)

 

What is the relationship between the IDs and the animals? The normal answer to "how do I get a thing based on ID" is "do a query for the information from the database and construct the object appropriately", but that doesn't seem right here.

Link to comment
Share on other sites

A factory is responsible for creating an instance of a given interface / abstract class based on some sort of criteria.

 

For example when you asked about using React\EventLoop\Factory::create, the reason for using it is so that it can return the best event loop implementation available. It inspects the environment for various extensions before returning a generic option.

 

In your animal case the factory would take some sort of data to identify what type of animal needs to be constructed and then do so accordingly. That might be an ID which is then looked up in the database or it might be an array with a specific member key. It might also be two separate arguments, one for the type and another for the context.

 

The main purpose is to offload the construction to a central place, similar to how dependency injection makes the container responsible for construction of objects. Your factory might start out as simple as something like:

public function getById($id){
    $data = $this->lookupIdInDb($id);
    $class = $data['class'];
    
    return new $class($data);
}
but in the future end up more complex as your requirements change.
Link to comment
Share on other sites

What is the relationship between the IDs and the animals? The normal answer to "how do I get a thing based on ID" is "do a query for the information from the database and construct the object appropriately", but that doesn't seem right here.

What makes you think it is abstract? :)  Please know I only do so as I feel it makes it easier for others to provide answers, and definitely do so to make things easier for myself.  I am using this pattern several places where the latest is a communication interface.  One implementation is bi-directional using sockets and the other is a simple cron which utilizes curl.

 

 

What is the relationship between the IDs and the animals? The normal answer to "how do I get a thing based on ID" is "do a query for the information from the database and construct the object appropriately", but that doesn't seem right here.

Good question which I should have better described.  I am using a super table with sub-tables for the more specific instances, so ID is the primary key and unique across all animals.  I could obviously query the database, but my dilemma is how to best create the objects which have both properties and methods.

 

In your animal case the factory would take some sort of data to identify what type of animal needs to be constructed and then do so accordingly. That might be an ID which is then looked up in the database or it might be an array with a specific member key. It might also be two separate arguments, one for the type and another for the context.

 

The main purpose is to offload the construction to a central place, similar to how dependency injection makes the container responsible for construction of objects. Your factory might start out as simple as something like:

public function getById($id){
    $data = $this->lookupIdInDb($id);
    $class = $data['class'];
    
    return new $class($data);
}

 

Yes, it is a primary key as I explained in response to requinix.  My question is where to offload it: whether in a single factory classes which is only responsible to creates objects for all types, or in one of the parent classes.  The problem with the parent class is it needs to extend and execute the animals class twice.

Link to comment
Share on other sites

My question is where to offload it: whether in a single factory classes which is only responsible to creates objects for all types, or in one of the parent classes.

Put it into it's own separate class.

 

class AnimalFactory {
    public function create($id){
        //do stuff...

        return $animal;
    }
}
Link to comment
Share on other sites

Put it into it's own separate class.

 

Will do.  I had put multiple create methods in a single class, but agree separate classes is much better.  I take it I want to make the class as low level as possible (i.e. don't use MammalFactory and ReptileFactory) providing that the method name is generic (i.e. create(), and not like I am doing below).

class Factory {
   public function createAnimal($id){
       //do stuff...

      return $animal;
   }

   public function createPlant($id){
       //do stuff...

      return $plant;
   }
}

Also, is a factory class a good application for a static class?

 

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.