Giter Club home page Giter Club logo

php-factory's Introduction

Factory

DEFUNCT

This project is not useful anymore. For purposes of useful type-checking, Abstract Factory Classes should use explicit method names for instantiation, rather than string-based instantiation. This fact negates any benefit that this class once offered.

For those who came here hoping to find a solution to their instantiation woes, the correct answer is something like this:

<?php
// FactoryInterface.php
namespace YourNamespace;

interface FactoryInterface {
    public function newMyObject();
    public function newYourObject();
}
?>


<?php
// Factory.php
namespace YourNamespace;

class Factory implements FactoryInterface {
    public function newMyObject() {
        return new MyObject(func_get_args()); 
    }

    public function newYourObject() {
        return new \OtherNamespace\YourObject(func_get_args());
    }
}
?>

You can use this same technique to execute static methods, get static class constants, etc. The line is blurry about what you should actually implement within the Factory class, but you can feel that out yourself.

A class that provides overridable instantiation of classes based on text descriptors rather than concrete class names.

If you don't know about Dependency Injection, there are plenty of resources on the internet about it. Please make sure you understand what this is for before using it.

The short version is that you should find a way to allow yourself and other programmers to change what concrete implementation of the requested functionality is being used at any given time without having to change the code that uses it.

I've chosen to implement this by creating a Factory class that is passed on construct to all of my other classes. When one of my objects needs to create a child object, it tells the factory what it wants, giving it the parameters it wants to be passed into the constructor, and the Factory gives it an instance of some class that was defined by the programmer when the application was configured.

The program itself won't always know what actual class of object this is, and in fact shouldn't care. What it should care about is that whatever object it gets back implements the interface it's expecting.

Usage

Here's a basic example of how this should be used (implementation is below):

// Get your factory instance
$f = MyFactory::getInstance();

// Use it to create a new app instance
$app = $f->new('app');

// Since you can't guarantee *what* app instance it gave you, you have to verify that it implements the interface you're expecting
if (!($app instanceof AppIWannaRun)) throw new RuntimeException("App returned by factory must be of type `AppIWannaRun`");

// Now run it
$app->run();

This is an extremely high-level example, but it demonstrates that

  1. you tell the factory what type of object you want (my implementation actually also allows for subtypes); and
  2. you verify that the instance that you get back implements the interface you're expecting.

You'll usually find more complex examples of this further inside the code. Consider this:

class MyApp {
    protected $factory;
    protected $db;

    // Here, you have to pass an instance of your factory to the app on construct, along with an instance of a DB
    public function __construct(FactoryInterface $f, DatabaseInterface $db) {
        $this->factory = $f;
        $this->db = $db;
    }

    // Do lots of stuff here ......

    // Now here's an internal method
    protected function getInternalThings() {
        // Get things from the DB
        $things = $this->db->query('SELECT * FROM `things`');

        // Create a thing collection
        $thingCollection = $this->factory->new('collection', 'things');

        // Iterate over the db results, adding things to the collection
        foreach($things as $thing) {
            $thingCollection[] = $this->factory->create('thing', null, $thing);
        }

        // Return the collection
        return $thingCollection;
    }

    // ...
}

In the above example, we used the factory to instantiate a collection, then used its special "create" method (which only works for classes that implement a static "create" method themselves) to "restore" instances of Things from data. This worked because somewhere, you told your factory what class represented a "collection of things", and what class represented a "thing".

You did that by....

Creating Your Type Map

In the above examples, we requested an object of type app, an object of type collection with subtype things, and several objects of type thing. But the factory isn't born knowing how to create these objects. You have to tell it. And you do this by subclassing Factory and overriding the getClass method.

To create a factory that can instantiate all these objects, here's what we do:

<?php
namespace MyNamespace;

class MyFactory extends \KS\Factory {
    // Signature has to match the original Factory::getClass method
    public function getClass(string $type, string $subtype=null) {
        // Return the class as a string including namespace
        if ($type == 'app') {
            // Always a good practice to check for subtype, even if you don't need one today
            if (!$subtype || $subtype == 'generic') return '\\MyNamespace\\App';
        }

        if ($type == 'collection') {
            if ($subtype == 'thing') return '\\MyNamespace\\ThingsCollection';
            // Notice, if we're asking for a subtype we don't know how to create, it falls through
        }

        if ($type == 'thing') {
            if (!$subtype || $subtype == 'generic') return '\\MyNamespace\\Thing';
        }

        // Fall through to parent
        return parent::getClass($type, $subtype);
    }
}

Sometimes you'll put this in a separate file. More often, though, this can go right in your front controller (usually index.php in a web app).

If you're writing something that someone may extend, it's a good idea to put your custom factory in its own class file. That way, someone else can extend it and avoid having to remap all the objects you've already mapped.

Conclusion

That's it! Hope it's useful to someone.

php-factory's People

Contributors

kael-shipman avatar

Watchers

 avatar  avatar  avatar

php-factory's Issues

Remove generic `new` method and make abstract

To conform to best practices for Factory pattern, this class should require that the programmer implement a concrete method for each class to instantiate. This method should have a return value corresponding to the desired Interface.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.