我正在构建一个非常小的MVC框架来增加我的PHP知识并挑战自己。我已经到了类开始依赖彼此工作的地步。 Dependency injection似乎是解决此问题的方法,并被big frameworks周围的一些人使用。
我在Github上找到了Bucket并且已经搞砸了一段时间才能理解基础知识。 然而我无法理解的是,何时创建容器是合适的?
制作一个大容器,包括可能需要的每个可能的课程对我来说似乎只是适得其反,我无法想象这是一个好习惯。这似乎至少是糟糕表现的秘诀。
另一种方法是制造多个容器,我仍然不知道如何不再需要那些臭气熏天的单身人士。
假设我有以下代码:
$session_container = new bucket_Container();
$session_container->create('Database');
$session_container->create('Database_Sessions');
$log_container = new bucket_Container();
$log_container->create('Database');
$log_container->create('Database_Log');
所以这里我们有两个容器,或者在本例中是 buckets ,它们是两个完全不同的用法,它们依赖于Database
类。
我的逻辑告诉我,上面的代码将创建Database
- 类的两个独立实例,这意味着我仍然必须使Database
- 类成为单例,以确保并发实例我的数据库连接没有发生?
这是正确的吗?
答案 0 :(得分:9)
我对具体的lib知之甚少,但假设它允许你使用工厂,让工厂返回相同的实例。
编辑:好的,这只是在Bucket GitHub索引页面上。
class MyFactory {
function new_PDO($container) {
return new PDO("mysql:host=localhost;dbname=addressbook", "root", "secret");
}
}
$bucket = new bucket_Container(new MyFactory());
$db = $bucket->get('pdo');
所以在你的情况下你可以做到:
class MyFactory {
private $pdo;
function new_Database($container) {
if($this->pdo){
return $this->pdo;
}
return $this->pdo = new PDO("mysql:host=localhost;dbname=addressbook", "root", "secret");
}
}
$factory = new MyFactory();
$session_container = new bucket_Container($factory);
$session_container->create('Database_Sessions');
$log_container = new bucket_Container($factory);
$log_container->create('Database_Log');
这样的事情。看起来不像火箭科学。
Edit2:我没有足够的代表来评论这个问题(有点傻),但是为了回应你的“模块化”问题:把容器想象成你应用程序的“粘合剂”。实际上,如果您有一个大型应用程序,您可能只想在应用程序的隔离部分“粘合”。这是一个有效的封装问题。但即便如此,您仍然需要一个能够在最高抽象级别处理注入的容器。如果您只是为应用程序的每个部分创建一个单独的容器,您最终会得到不必要的实例重复,或者您必须应用另一级别的实例管理,这不会以任何方式改进封装:您仍然在应用程序的不同部分之间共享实例。
我的建议是在引导程序级别使用单个容器。如果您想为应用程序的特定部分(模块,插件等)添加封装,请使用“子容器”。子容器从父容器继承实例,但父容器对该子容器一无所知(就他而言,他仍然是单身汉;))。可能是Bucket默认支持这个,我知道其他DI容器。如果没有,使用Decorator实现起来非常容易。想象一下这样的事情:
class MyContainerType extends bucket_Container {
private $_parent;
private $_subject;
public function __construct($factory = null, bucket_Container $parent = null){
$this->_parent = $parent;
$this->_subject = new bucket_Container($factory);
}
public function get($key){
$value = $this->_subject->get($key);
if($value){
return $value;
}
return $this->_parent->get($key);
}
/**
* Override and delegation of all other methods
*/
}
答案 1 :(得分:6)
制作一个大容器,包括可能需要的每个可能的课程,似乎对我来说只是适得其反,我无法想象这是一个好习惯。这似乎至少是糟糕表现的秘诀。
恰恰相反。这正是你用di容器做的事情。容器只会按需实例化对象,因此通过它管理所有单例类几乎没有任何开销。
di的最大问题是区分共享对象(通常认为是单例的东西)和瞬态对象(通过正常的应用程序流具有大量实例的对象)。前者很容易通过di管理。后者并不适合。清楚地区分这两种“对象”可能看起来有点麻烦,但使用di容器确实是非常有益的副作用。
答案 2 :(得分:0)
如果您担心多个同时连接,可以使用mysql_pconnect()或equivelant来处理您正在使用的数据库。它将检查连接是否已打开,如果是,则使用现有连接。
就容器问题而言,我已经看到它以两种方式完成,您似乎都知道这两种方式。第一种方法是让框架读取您的数据库模式并在每个表之前创建类。我个人不喜欢这种方法。 Symfony就是这样做的一个框架(通过使用ORM原则)。
我看到的更优选的方法是拥有一个通用容器,它基本上为你提供一个表,列和一个动作的sql。这是codeIgniter采用的方法:
$query = $this->db->get('mytable');
$query = $this->db->get_where('mytable', array('id' => $id), $limit, $offset);