我正在开发OO PHP中的社交网络类型项目,我不想使用现有的框架。这个项目的主要目的是帮助我了解更多的东西。
这个问题更多的是关于依赖注入。
假设我有这些课程:
核心课程 - 在应用中执行操作的一些核心方法
配置类 - 加载网站配置资料
数据库类 - 连接到mysql并执行所有与数据库相关的内容
记录器类 - 用于记录错误和调试信息
验证码类 - 表格上的验证码
会话类 - 启动会话启动并添加,删除,获取要在应用中使用的会话变量
缓存类 - 类似于会话类但是对于缓存项(文件缓存,内存缓存,apc缓存。我甚至有一天可能会将我的会话内容添加到此类中,因为所有这些缓存都可以使用相同类型的方法)
上面的所有类很可能会在我的应用中的每个页面加载时使用(我可能会错过更多将在稍后添加的类)
现在除了需要注入大多数其他类的上述类之外,我还会有更多的类。我将有一个名为模块的部分,它将具有类似......的内容。
帐户类 - 创建新用户,对用户进行身份验证,将用户登录和退出应用,更新用户设置等等。
用户类 - 显示用户个人资料,在线显示用户,新用户,显示网站用户的所有内容
论坛类 - 将用于论坛部分
博客课程 - 适用于博客部分
照片类 - 所有照片相关的东西
评论类 - 处理照片和个人资料的评论
对于网站的不同部分,将会有更多类型的课程 上面列出的第二组类很可能需要将第一组中的大多数类注入其中。
那么我应该使用注册表来存储第一组类中的对象,并将注册表注入第二组类中的所有类对象?
或者我应该使用构造函数来加载它们?在这个例子中,有7个对象可以注入到其他类中,这似乎很多。我错了吗?
--- --- EDIT
我知道单身模式,但我不认为这是我最好的选择
---编辑2 ---
正如一些人提到的,需要传递多达7个对象似乎很多,这就是为什么我在寻找建议。幸运的是,这个项目处于起步阶段,所以现在是改变结构的时候了。
一个例子是我论坛部分的一个类。论坛类需要访问会话数据,可能的缓存数据,配置对象,数据库对象。我错了吗?
答案 0 :(得分:11)
或者我应该使用构造函数来加载它们?在这个例子中,有7个对象可以注入到其他类中,这似乎很多。我是不是错了?
当您开始需要注入那么多对象时,您需要询问接收它们的对象是否负责太多。它可以分解成更小的部分吗?
如果确实不能,那么考虑将相关对象封装在另一个类中。也许您的会话,记录器和配置对象可以注入App
或Application
对象?
编辑:我注意到目前为止大多数其他答案都在讨论单身人士。请注意,单身人士是DI的敌人。伟大的Google技术人员就此here进行了讨论。
编辑2:
封装的意思是,不是将Session
,Logger
,Config
等全部注入您的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);
这样,您可以对一组对象进行分组,并将相同的上下文传递给其他几个库对象。