I'm pondering one of two different ways of implementing the factory pattern in PHP. I don't know if these variants have proper names so for now I'm going to call them internal factory and external factory.
Internal factory: The factory method is implemented in the class itself as a static public method
<?php
class Foo
{
protected
$loadedProps = false;
public static factory ($id)
{
$class = get_called_class ();
$item = new $class ($id);
if ($item -> loadedProps ())
{
return ($item);
}
}
public function loadedProps ()
{
return ($this -> loadedProps);
}
protected function loadPropsFromDB ($id)
{
// Some SQL logic goes here
}
protected function __construct ($id)
{
$this -> loadedProps = $this -> loadPropsFromDB ($id);
}
}
?>
External factory: The factory and the items it initializes are implemented as separate entities
<?php
class Foo
{
protected
$loadedProps = false;
public function loadedProps ()
{
return ($this -> loadedProps);
}
protected function loadPropsFromDB ($id)
{
// Some SQL logic goes here
}
public function __construct ($id)
{
$this -> loadedProps = $this -> loadPropsFromDB ($id);
}
}
abstract class FooFactory
{
public static factory ($id)
{
$item = new Foo ($id);
if ($item -> loadedProps ())
{
return ($item);
}
}
}
?>
Now it seems to me that each has its merits.
The former allows you to hide the constructor from the outside world. This means the only way you could create a Foo object is via the factory. If the item's state can't be loaded from the DB then the factory will return NULL which you can check for easily in code.
if ($item = Foo::factory ($id))
{
// ...
}
else
{
// The item failed to load. Handle error here
}
The factory can also create objects of any subclass of Foo without any modification.
However, it seems to have some drawbacks. First the class has to implement the factory, which might be putting responsibilities in the class that really belong elsewhere. The internal factory version certainly results in a larger class than the external factory version.
As for the external factory, well it's cleaner as the factory isn't in the class itself and I don't have to worry about a class taking on more responsibility than it should. The external factory may also be better suited to dependency injection.
However, it has its own set of drawbacks. First and foremost, the constructor of the item to be built in the factory must be public as PHP doesn't have a concept of packages and no 'package' protection level for class members. This means there's nothing stopping a coder from just doing new Foo () and bypassing the factory (This may make unit testing easier, though).
The other problem is that FooFactory can only create Foo objects, not any of its subclasses. This could be got around by adding another parameter to the FooFactory to specify a class name, but then the factory will have to do internal checks that the specified object class is in fact a descendant of Foo.
So basically, what are the relative merits of the two approaches, and which would you recommend?
Also, if they have more proper names than internal or external factory, I'd like to know them.
Delegating manufacturing to an outside source allows companies to hedge their bets on innovative new products by shifting their spend on marketing rather than hard assets. December 18, 2020.
A supplier who is a part of the same company as its customer is an internal supplier. They may provide products, services, or other resources. They are the upstream processes and the support groups that provide their coworkers with the tools, materials, and work-in-process to do their jobs.
External Supplier means any agent, sub-agent, subcontractor, nominee, custodian, trustee, administrator, third party supplier or other service provider appointed or engaged from time to time by the Suppliers , to supply or provide products and services to the Suppliers in connection with the ifsinvest Platform Products ...
External sourcing refers to the procurement of goods necessary for business operations from third-party organizations. It is often a good idea, especially in cases where it is cheaper and easier to secure the items that way.
Actually, Factories are established Creational Design Patterns, so you can read about their purpose in the GoF Book or at Sourcemaking:
There is some overlap in these patterns, especially between Factory Method, Abstract Factory and Builder and the distinction is even more blurry when you are not creating families of objects, but just one type of object. So yes, for simplicity, let's assume internal and external factory are the right terms.
Personally, I would always favor an External Factory over an Internal Factory due to the reason you already gave: I can use Dependency Injection and can separate the responsibilities. Since static methods are death to testability and can be considered harmful due to the coupling they introduce, I would make the Factory a real object though instead and use non-static methods.
The two drawbacks you mention are not really drawbacks at all.
I cannot think of a reason why I would want to prevent a developer from instantiating objects that a Factory creates. In fact, when Unit-Testing I will want to create that object on my own and replace any dependencies with Mocks and Stubs. I also dont believe that developers should be babysitted too much. Given the script nature of PHP, changing the ctor from private to public is way too easy to be effectively prevented anyways.
As for the other problem of the Factory being unable to create other classes, that's not true. The idea of a Factory actually is to create various types of a family of objects. Even the Factory Method is explicitly allowed to create subclasses. Whether you implement that with a switch/case or with various methods on the factory is up to you. There is also no reason not to combine Factories with Builders or having Factories of Factories that in turn encapsulate the logic to create an object. Thus eliminating the need for any of the internal checks you mention (which could also be met with typehints).
The other viable alternative to Factories would be to use a Dependency Injection Container, like the Symfony Components DIC and manage your objects through that container mostly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With