PHP – Why you should use the Factory Method Pattern and how you should do it

PHP – Why you should use the Factory Method Pattern and how you should do it

This article assumes you’re using PHP version 5.3 or above. If you are not, you should note that “the PHP 5.2 series is NOT supported anymore” and you should really upgrade.

The Factory method pattern is a marvelous idea. It is designed to reduce the number of instances in which you use the ‘new’ keyword in your code. An example of its use as as follows:

  1. <?php
  3. class SomeClass {
  4. protected $foo;
  6. protected function __construct($foo) {
  7. $this->foo = $foo;
  8. }
  10. public static function factory($foo = 'default') {
  11. return new static($foo);
  12. }
  14. public function get_foo() {
  15. return $this->foo;
  16. }
  17. }
  19. echo SomeClass::factory()->get_foo();

There are a number of points to note in this code. The first of these is that the constructor is protected. This is a feature of PHP 5.3 and above which prevents a class being instantiated using the new keyword. You’ll also notice that the default value for $foo is only specified on the factory() method. This is simply because specifying it on the constructor would be redundant as the constructor will only ever be called by the factory method. There’s a method called factory() which does nothing but return a new instance of ‘static’ (the current class). There’s also an example of how the factory is used and you’ll see that the get_foo() method is chained on. This is a useful and elegant feature which you would not get using ‘new’.

So what are the other advantages? Well, the constructor cannot return a value, whereas the factory method can. This gives you significantly more control over what you return. For example, if object construction failed, you can return a false or null value. I can’t think of a use case offhand for this and it’s probably better to throw an exception (which can also be done from the constructor) so that the successful return of a factory is always an object.

Consider a scenario like this… You have an API which you have been using for a while and decide it’s time to make a new/better featured one. Many of your sites rely on this API and, as such, you need to migrate them slowly. If you had used a factory() method in the first instance, there would be no issues doing this as you can simply change the type of object which it returns. For example…

  1. <?php
  3. class API {
  4. protected $site_id;
  6. protected function __construct($site_id) {
  7. $this->site_id = $site_id;
  8. }
  10. public static function factory($site_id) {
  11. if (Site::uses_new_api($site_id)) {
  12. return NewAPI::factory($site_id);
  13. }
  15. return new static($site_id);
  16. }
  18. public function get_info() {
  19. return file_get_contents("{$this->site_id}");
  20. }
  21. }
  23. class NewAPI extends API {
  24. public static function factory($site_id) {
  25. return new static($site_id);
  26. }
  28. public function get_more_info() {
  29. return file_get_contents("{$this->site_id}");
  30. }
  31. }

You’ll see here that we’ve created a NewAPI class which extends the original API and adds functionality to it. We’ve also modified the factory() method of API to use a fictitious Site::uses_new_api() function to determine if the site uses the new API – if it does, we return an instance of it. This prevents us having to modify the code of the sites which use this class in order to migrate them to the new API.

Factory methods also make your life a little easier as you can have multiple methods which instantiate the object in different ways. For example:

  1. <?php
  3. class Image {
  4. protected $type;
  5. protected $filename;
  7. protected __construct($type, $filename) {
  8. $this->type = $type;
  9. $this->filename = $filename;
  10. }
  12. public static function jpeg_factory($filename) {
  13. return new static('jpeg', $filename);
  14. }
  16. public static function gif_factory($filename) {
  17. return new static('gif', $filename);
  18. }
  20. public function get_readable_type() {
  21. if ($this->type == 'jpeg') {
  22. return 'The image type is JPEG';
  23. }
  24. if ($this->type == 'gif') {
  25. return 'The image type is Graphics Interchange Format';
  26. }
  27. }
  28. }

You’ll see in this example we have two factory methods –  jpeg_factory() and gif_factory(). These only take in the filename and serve to ensure that only ‘gif’ and ‘jpeg’ can end up in the internal $type variable. Again, you could use exceptions to signify there being an error in what the passes into the class, but this is a very basic example.

So, the advantages we’ve established are:

  • You can prevent the use of ‘new’ and thus have more control over how a class is instantiated
  • You can chain methods onto the end of a factory() call to avoid the need for multiple lines of code (or even storing a reference to an object at all)
  • Code instantly becomes more maintainable as you have control over what is returned from the factory methods
  • You can have multiple factory methods to better control and simplify the ways in which the object is instantiated

Leave a Reply

Your email address will not be published. Required fields are marked *