这是通过构造函数注入对象的太多依赖项吗?

时间:2011-01-18 20:41:13

标签: php dependency-injection dependencies registry

我正在开发OO PHP中的社交网络类型项目,我不想使用现有的框架。这个项目的主要目的是帮助我了解更多的东西。

这个问题更多的是关于依赖注入。

假设我有这些课程:

核心课程 - 在应用中执行操作的一些核心方法
配置类 - 加载网站配置资料
数据库类 - 连接到mysql并执行所有与数据库相关的内容 记录器类 - 用于记录错误和调试信息
验证码类 - 表格上的验证码 会话类 - 启动会话启动并添加,删除,获取要在应用中使用的会话变量
缓存类 - 类似于会话类但是对于缓存项(文件缓存,内存缓存,apc缓存。我甚至有一天可能会将我的会话内容添加到此类中,因为所有这些缓存都可以使用相同类型的方法)

上面的所有类很可能会在我的应用中的每个页面加载时使用(我可能会错过更多将在稍后添加的类)

现在除了需要注入大多数其他类的上述类之外,我还会有更多的类。我将有一个名为模块的部分,它将具有类似......的内容。

帐户类 - 创建新用户,对用户进行身份验证,将用户登录和退出应用,更新用户设置等等。
用户类 - 显示用户个人资料,在线显示用户,新用户,显示网站用户的所有内容
论坛类 - 将用于论坛部分
博客课程 - 适用于博客部分
照片类 - 所有照片相关的东西
评论类 - 处理照片和个人资料的评论

对于网站的不同部分,将会有更多类型的课程 上面列出的第二组类很可能需要将第一组中的大多数类注入其中。

那么我应该使用注册表来存储第一组类中的对象,并将注册表注入第二组类中的所有类对象?

或者我应该使用构造函数来加载它们?在这个例子中,有7个对象可以注入到其他类中,这似乎很多。我错了吗?

--- --- EDIT
我知道单身模式,但我不认为这是我最好的选择

---编辑2 ---
正如一些人提到的,需要传递多达7个对象似乎很多,这就是为什么我在寻找建议。幸运的是,这个项目处于起步阶段,所以现在是改变结构的时候了。

一个例子是我论坛部分的一个类。论坛类需要访问会话数据,可能的缓存数据,配置对象,数据库对象。我错了吗?

2 个答案:

答案 0 :(得分:11)

  

或者我应该使用构造函数来加载它们?在这个例子中,有7个对象可以注入到其他类中,这似乎很多。我是不是错了?

当您开始需要注入那么多对象时,您需要询问接收它们的对象是否负责太多。它可以分解成更小的部分吗?

如果确实不能,那么考虑将相关对象封装在另一个类中。也许您的会话,记录器和配置对象可以注入AppApplication对象?

编辑:我注意到目前为止大多数其他答案都在讨论单身人士。请注意,单身人士是DI的敌人。伟大的Google技术人员就此here进行了讨论。

编辑2:

封装的意思是,不是将SessionLoggerConfig等全部注入您的Account类,也许应将它们注入Application类,Application个实例可以注入Account

说实话,这仍然是我的一部分,我正在把我包裹起来,但我开始看到的是:你应该只注入它将直接需要的对象Account操纵。这些对象可能需要操纵其他对象,但Account不需要知道它。


通常,你会发现你甚至不需要像你想象的那么多“层”。如果您发现在整个地方注入了东西,请考虑各种方法来重新构建您的应用程序。让我从模型上的Pylons文档中提取一段代码:

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

(不要担心meta.Session ...基本上它处理与数据库的交互。)

在这里,我们实例化一个Person,设置它的几个属性,然后保存它。您会注意到Person类知道没有关于数据库,实际上甚至没有save()方法。相反,我们将它传递给保存它的数据库类(meta.Session)。我们将Person逻辑的关注点与数据库代码分开了。 meta.Session可以使用十几种不同的方式,但只要它知道如何阅读Person,我们就可以了。 (在这种情况下,在应用程序初始化期间,它会读取所有应用程序模型的定义)。


好的,我要总结一下,因为我在这里显然已经漫步了很多。你的问题没有一个“正确”的答案(据我所知,但我不会以任何方式宣称成为DI专家)。

是的,注入几个对象可能表明需要进行重组,但您需要考虑以下几点:

  • 接受注射的物体是否负责过多;可以分开吗?
  • 您是否只注入收件人类将直接需要使用的对象?
  • 你是否注入错误的方向;你的Account对象可以注入Database而不是相反吗?

答案 1 :(得分:-2)

我一直更喜欢注册表方法。

原因:

  • 更大的范围
  • 简单易懂
  • 构造函数不会将对象作为变量(允许更好地使用)
  • 允许您在课程中使用更多对象,随时随地抓取您需要的内容。

虽然还有另一种方法可以尝试使用单例模式,但在每个类中都有一个跟踪自己对象的方法。

例如,创建一个如此的界面:

interface ISingleton
{
    public static getInstnace();
}

然后,对于每个对象,您可以添加用于获取实例的方法。

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

这样,您始终保持对象的相同实例以及具有全局范围。

示例:

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

希望这有帮助。


你可以采用另一种方式来创建一个上下文类,让我试着解释一下:

该类的行为类似于注册表,但是一次性的,例如:

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;
    }
}

并使用它作为容器来保存你的对象:

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

$UserObject = new RegularUser($Context);

这样,您可以对一组对象进行分组,并将相同的上下文传递给其他几个库对象。