Re: Re: PEAR2 Coding standards, Autoloading and Namespaces
| From: | Helgi Þormar Þorbjörnsson | Date: | Sat, 12 Apr 2008 22:33:28 +0000 |
| Subject: | Re: Re: PEAR2 Coding standards, Autoloading and Namespaces | ||
| References: | 1 2 3 4 5 6 7 8 9 | Groups: | php.pear.dev |
| Request: | Send a blank email to pear-dev+get-49779@lists.php.net to get a copy of this message | ||
On Sat, Apr 12, 2008 at 10:14 PM, Jeff Moore <jeff@procata.com> wrote:
>
> On Apr 8, 2008, at 1:56 AM, Christian Weiske wrote:
>
>
> > I'd like to think outside the box, if you could tell me the solution
> > for following problem:
> > - Package MDB3
> > - Driver packages MDB3_Sqlite, MDB3_MySql etc
> >
> > How do you load those drivers? Currently it was as easy as checking the
> > existence of $path/MDB2/Driver/Sqlite.php and include the file with
> > that name.
> > If every package uses his own namespace, how do you load such a driver?
> > Where do you store it in the file system so that it can be easily found?
> >
> > Making exceptions to the packagename->namespace rule would work, but
> > defeat the purpose of the whole idea.
> >
>
> Well, I have an answer and its somewhat out of the box, too. :)
>
> Rather than having a function that "loads" a driver based that driver being
> in a specific directory (or based on a specific class name), you can use
> dependency injection to pass the plugin into the class that will use it.
>
>
> Driver Pattern:
>
> class Cache {
> static function getCache($driver, $options = array()) {
> $file = '/drivers/' . $driver . '.php';
> require_once $file;
> $classname = ucfirst($driver) . 'Cache';
> return new $classname($options);
> }
> }
>
> class CacheDriverCommon {
> protected $options;
> function __construct($options) {
> $this->options = $options;
> }
> }
>
> class FileCache extends CacheDriverCommon {}
>
> class FeedFetcher {
> protected $cache;
> function __construct($options = array()) {
> $this->cache = Cache::getCache(
> $options['driver'],
> $options);
> }
>
> // Do something with $this->cache later
> }
>
> $fetcher = new FeedFetcher(array('cache'=>'file',
> 'cache_path'=>'/tmp'));
>
>
> With Dependency Injection:
>
>
> interface Cache {
> // ...
> }
>
> class FileCache implements Cache {
> protected $path;
> function __construct($path) {
> $this->path = $path;
> }
> }
>
> class FeedFetcher {
> protected $cache;
> function __construct(Cache $cache) {
> $this->cache = $cache;
> }
> }
>
> $fetcher = new FeedFetcher(new FileCache('/tmp'););
>
> The DI version is more modular, more testable (easier to pass a mock
> object), and getting rid of the options array has some advantages, such as
> being easier to document, and autocompletion advice in IDEs becomes
> available for constructor parameters.
You didn't really answer the namespace issue;
All this new Foobar() sounds really cute until you have long class names
$oRestoReview = Foo_Content::createType('RestaurantReview')
->addAttribute(new Foo_Attribute(
array('name' => 'dateEaten', 'typeId' =>
FOO_CONTENT_ATTR_TYPE_DATE)))
->addAttribute(new Foo_Attribute(
array('name' => 'dishName', 'typeId' =>
FOO_CONTENT_ATTR_TYPE_TEXT)))
->addAttribute(new Foo_Attribute(
array('name' => 'overallRating', 'typeId' =>
FOO_CONTENT_ATTR_TYPE_FLOAT)))
->save();
This is an example from a real codebase taking this fun approach,
start becoming messy soon, don't it ?
I'll agree that DI has some benefits over factory pattern; Just two
ways of solving this problem, somehow feels like we are effectively
excluding factory to a certain extent with this change while the old
approach would have supported both ways, at least should have but I
might be farting around at this point.
- Helgi