10

I am working on a social network type project in OO PHP and I do not want to use an existing framework. The main point of doing this project is to help me learn about more stuff.

This question is more about dependency injection.

Let's say I have these classes:

core class - some core methods to do stuff in the app
config class - loads site config stuff
database class - connects to mysql and does all database related stuff
logger class - used to log errors and debug info
captcha class - for captcha on forms
session class - initiates a session start and adds, removes, gets session variables to use in the app
cache class - similar to session class but for caches items (files cache, memcache, apc cache. I may even add my session stuff to this class someday since all these cache's can use the same type of methods)

All the classes above will most likely be used on every page load in my app (I probably missed some more classes that will be added later on)

Now in addition to the above classes which will need to be injected into most other classes, I will have many many more classes. I will have a secion called modules which will have stuff like...

account class - creates new users, authenticates users, logs users in and out of the app, update user settings, and much more.
users class - shows users profiles, shows users online, new users, all stuff to show users of the site
forums class - will be for the forums section
blogs class - for the blogs section
photos class - all photo related stuff
comments class - handles comments for photos and profiles

There will be many more of these type of classes for different sections of the site.
This second set of classes listed above for sections will most likely require most of the classes from the first set to be injected into them.

So should I use a registry to store the objects from the first set of classes and just inject the registry into all the class objects in the second set of classes?

Or should I use the constructor to load them? In this example there would be like 7 objects to inject into the other classes, that seems like a lot. Am I going about this wrong?

---EDIT---
I am aware of the singleton pattern but I don't think it is my best option here

---EDIT 2---
As some have mention, needing to pass in as much as 7 objects does seem like a LOT and that is why I am looking for suggestions. Luckily for me this project is at the beginning stages so now is the time for changes to the structure.

An example would be a class in my forums section. The forums class would need access to session data, possible cached data, the configs object, database object. AM I going about this the wrong way?

volting
  • 16,773
  • 7
  • 36
  • 54
JasonDavis
  • 48,204
  • 100
  • 318
  • 537
  • Related: http://stackoverflow.com/questions/2420193/dependency-injection-constructor-madness – Mark Seemann Jan 18 '11 at 20:49
  • Deleted my reply, because you mentioned it as well. Go with the registry pattern. – mhitza Jan 18 '11 at 20:50
  • 1
    If your objects need 7 objects to be injected just to function, I'd challenge the underlying architecture of those classes itself. Ideally, you should only need a couple of objects. Alternatively, Dependency Injection containers may alleviate some of your stress.. personally, I'm not a fan. – CaseySoftware Jan 18 '11 at 21:14
  • @CaseySoftware that is what I am thinking too but i'm not sure – JasonDavis Jan 18 '11 at 21:23
  • Have you thought about using Context's to pass objects around, such as `new Context($db,$logger,$session,$filter)` and then doing `new Account($Context)` ? – RobertPitt Jan 18 '11 at 21:37
  • Classes like "logger" are a good candidate for the [observer pattern](http://stackoverflow.com/questions/3213423/php-how-could-i-make-this-class-better-suggestions-feedback-welcome/3213700#3213700). – John Conde Jun 10 '11 at 20:54

2 Answers2

12

Or should I use the constructor to load them? In this example there would be like 7 objects to inject into the other classes, that seems like a lot. Am I going about this wrong?

When you start needing to inject that many objects, you need to ask whether the object that receives them is responsible for too much. Can it be broken down into smaller pieces?

If it really can't, then consider encapsulating related objects in another class. Perhaps your session, logger, and config objects can be injected into an App or Application object?

Edit: I notice most of the other answers so far talk about Singletons. Note that Singletons are the enemy of DI. Great Google tech talk on this here.

Edit 2:

What I mean by encapsulating is, instead of injecting Session, Logger, Config, etc. all into your Account class, maybe they should be injected into an Application class, and an Application instance can be injected into Account.

To be honest, this is still a piece of DI I'm wrapping me head around, but what I'm starting to see is this: You should only inject into Account the objects it will directly need to manipulate. Those objects may need to manipulate yet other objects, but Account doesn't need to know about it.


Often, you'll find you don't even need as many "layers" as you thought, though. If you find you're injecting stuff all over the place, consider various ways of re-structuring your application. Let me pull a piece of code from the Pylons documentation on the model:

a = model.Person()
a.name = "Aaa"
a.email = "aaa@example.com"
meta.Session.add(a)

(Don't worry about meta.Session... basically it handles interaction with the database.)

Here, we instantiate a Person, set a couple of its attributes, and then save it. You'll notice the Person class knows nothing about the database, and in fact doesn't even have a save() method. Instead, we pass it to the database class (meta.Session) which saves it. We've separated concern for Person logic from the database code. meta.Session could work a dozen different ways, but as long as it knows how to read Person, we're fine. (in this case, during application initialization, it read the definition of all the application's models).


Okay, I'm going to sum up, because I've obviously rambled a lot here. There is no one "correct" answer to your question (as far as I know, but I don't proclaim to be a DI expert by any means).

Yes, injecting several objects is probably indication of some need to restructure, but you have a number of considerations to make:

  • Is the object receiving the injection responsible for too much; can it be separated out?
  • Are you only injecting objects that the recipient class will directly need to utilize?
  • Are you injecting the wrong direction; can your Account object be injected into your Database instead of the other way around?
keithjgrant
  • 12,421
  • 6
  • 54
  • 88
  • I added a little to my OP, but what do you mean by "encapsulating related objects in another class" – JasonDavis Jan 18 '11 at 21:24
  • 1
    +1 The advantage of dependency injection is so that things like the Logger, which the Account class shouldn't know how to create, can be provided from elsewhere. If many of these things can be instantiated during some configurable and environment-aware initialization of your Application class, then injecting the Application gives you a nice tidy package. And you won't have to change every constructor in your project when you add a new, widely used class. – grossvogel Jan 18 '11 at 21:48
  • 1
    I'm not a fan of singletons myself, if we are talking about the GOF singlton. However having a single instance of certain objects is not an anti-pattern per se; in fact, many DI containers allow registering instances as (non-GOF) singletons. I would also argue that Session, Logger, and Config are cross-cutting concerns, and may not be the best candidates for DI. (All that aside, +1 for your answer :). – Phil Sandler Jan 18 '11 at 21:52
  • @phil-sandler: Totally agree. Nothing wrong with only instantiating one instance of a class. It's just that class itself does not enforce it. What do you mean by "cross-cutting"? – keithjgrant Jan 18 '11 at 21:56
  • @keithjgrant: Cross-cutting essentially means something that is used across much/all of the system and is not specific to any particular module/domain/layer. Logging and Security are common examples. Pretty good definition here: http://en.wikipedia.org/wiki/Cross-cutting_concern. – Phil Sandler Jan 18 '11 at 22:07
  • @keithjgrant, thanks for the update. I understand what you mean by an application type object. That is pretty much what I thought a registry was. – JasonDavis Jan 18 '11 at 22:37
  • @Phil_Sandler "I would also argue that Session, Logger, and Config are cross-cutting concerns, and may not be the best candidates for DI" You are right in they are objects that will need to be used sitewide, but for example the session object will obviously hold session key/values so even though I create a session object in the main app or even just for example let's say there is a header file included everywhere, to access that session object inside of another class, it will have to be injected right? – JasonDavis Jan 18 '11 at 22:40
  • @jasondavis: this is a question that does not have one answer--in fact, I see a lot of differing opinions on this in the community. Injection is one approach and is nothing wrong with doing it that way. Some answers here on other this and other approaches: http://stackoverflow.com/questions/3524112/how-to-deal-with-cross-cutting-concerns-in-a-oo-application-use-singleton-depen. – Phil Sandler Jan 18 '11 at 22:50
  • I was just looking over the kohana framework and it seems in an example app that they are using public static functions for some things like Kohana::lang('core.stats_footer') and a singleton for certain items like sessions – JasonDavis Jan 19 '11 at 00:13
  • 1
    More food for thought: http://blog.ploeh.dk/2010/02/02/RefactoringToAggregateServices.aspx – keithjgrant Jan 19 '11 at 00:35
-2

I've always preferred the Registry approach.

Reasons:

  • Larger Scope
  • Simple to get
  • constructors are freed from having objects as variables (allows better usage)
  • Allows you use more objects in your classes as your free to grab what you need, when you want.

Although there is another method you can try and thats using the singleton pattern, where you would have a method in every class that keeps track of its own object.

Fors example, create an interface like so:

interface ISingleton
{
    public static getInstnace();
}

Then for every object you can add the method that is used for the getting of the instance.

class SomeObject implements ISingleton, ISomeThingElse
{
    private static $_instance;
    public static getInstance()
    {
        if(self::$_instance === null)
        {
           self::$_instance = new SomeObject(); 
        }
        return self::$_instance;
    }
}

This way your always keeping your same instance of the object as well as having a global scope.

Example:

public function GetUser($id)
{
    return Database::getInstance()->fetchUserById($id);
}

Hope this helps.


There is another way you can do and that's creating a context class, let me try and explain:

The class would act like a registry but disposable, for example:

class Context
{
    private $objects = array();

    public function Add($key,$Object)
    {
        $this->objects[$key] = $object;
    }

    public function __get($key)
    {
        return isset($this->objects[$key]) ? $this->objects[$key] : null;
    }
}

And use that as a container to hold your objects like so:

$Context = new Context();
$Context->Add("database",$database);
$Context->Add("logger",$logger);
$Context->Add("session",$session);

$UserObject = new RegularUser($Context);

This way you can group a set of objects and pass the same context into several other library objects.

RobertPitt
  • 56,863
  • 21
  • 114
  • 161
  • 10
    -1 This is the antithesis of dependency injection, and is a *horrible* design decision. – cdhowie Jan 18 '11 at 20:58
  • Please post an answer explaining your proposal. – RobertPitt Jan 18 '11 at 21:01
  • I have used code like this in the past but I am hoping to improve my code so I am looking for other alternatives – JasonDavis Jan 18 '11 at 21:26
  • Thats perfectly fine, I thought I would throw a technique in the mix for you. – RobertPitt Jan 18 '11 at 21:31
  • 1
    Also, i would love to see @cdhowie's implementation as he sounds like he has a better view on the matter :) .. Knowledge is power! – RobertPitt Jan 18 '11 at 21:32
  • My approach would be pretty close to keithjgrant's -- if many classes have roughly the same dependencies, these can be bundled into some kind of application/context object. You get some of the benefits of the singleton approach (the context contains all the required objects) without actually using the awful singleton pattern. – cdhowie Jan 18 '11 at 22:19
  • I haven't heard of this "Context" until now, however, the example you provided of this Context is pretty much just like I thought when I mention using a registry, is a registry different from a context? – JasonDavis Jan 18 '11 at 22:27
  • I did specify that it was pretty much like the registry nomination but as an alternative its better than nothing, I don't believe that the context is an official method of dependency collection but ive used it in the past and i have been happy. – RobertPitt Jan 18 '11 at 22:29
  • @cdhowie, I did post something similar as well – RobertPitt Jan 18 '11 at 22:31