4

我正在开发一个基于依赖注入的 PHP 框架。我的数据对象是可注入的组件,就像其他任何组件一样。

我有一个抽象 DAO 类,每个模型都应该扩展它,它具有:

  • 基本的crud方法
  • 对 DI 容器的引用,用于实例化对象

简而言之,事情就是这样

abstract class AbstractDao {
  protected $fields;
  protected $container; // This is the (injected) DI container, used to create instances.
  protected $driver; // The injected database driver (i.e. PDO)
  public function insert() {
    // Insert implementation
    // Insert current instance.
  }
  public function fetch($id) {
    // Fetch implementation
    // Fetches a row and sets fields on current instance
  }
  public function fetchAll() {
    // Performs a select * query on database driver
    // Iterates through results, and creates an instance
    // for each result using $container, like this:
    foreach ($results as $row) {
      // I can't just make $instances[] = new Something(), or all the
      // dependency injection thing would mess up.
      $instances[] = $this->container->get('someDao');
    }
    return $instances;
  }
  // Other methods.
}

class Book extends AbstractDao {
  protected $fields = array('field', 'definition', 'goes', 'here',);
  // No special behaviour is needed, so we can keep default
  // abstract implementation without overriding.
}

我的问题:每个数据对象实现(一本书、一个人、一个用户等)都必须扩展我的 AbstractDao 对象,因此它将承担$driver 和 $container 的重量。此外,由于 $fields 属性是在实例级别定义的,因此每个数据对象都有自己的,增加了更多开销。

我担心在处理大数据集时,就性能而言,此解决方案可能会导致非常昂贵的解决方案。我知道对象只会被引用,而不是被克隆,但开销可能会非常高。

我想到的几个解决方案是

  • 使用静态方法实现,可以减少子类的开销
  • 不要让我的 Daos 扩展上面提到的 AbstractDao,那应该成为一种 DaoProvider。在这种情况下,对于每种方法,我都应该传入实例(我不太喜欢的东西)

我不太喜欢这些解决方案...首先我不喜欢使用静态的东西,因为它们与注入的整个想法有点冲突。其次,我不喜欢删除 dao 子类模式的想法。

任何好主意将不胜感激,谢谢。

=== 编辑 ===

我又想到了一件事。我不喜欢第二种方法(“dao 提供者”)是提供者必须对 Dao 字段执行操作(设置值、设置状态、设置 isDirty 等),因此必须使字段可以从外部访问. 使用子类化方法可以保持那些受保护的或私有的。

=== /编辑 ===

4

1 回答 1

2

我建议您创建一个 DAO 接口来声明 DAO 实现必须定义的行为。现在,在每个具体的 DAO 实现中,您都可以定义您的$driver,$container$fields实例字段。

之后,您可能希望创建一个AbstractModel每个具体模型都应扩展的类,以便您的AbstractModel模型和具体模型都将“与数据访问无关”。该AbstractModel课程最终将如下所示:

/*
* an AbstractModel
*/
abstract class AbstractModel {

    protected $daoImpl;

    function __construct(DAOInterface $daoImpl) {
        $this->daoImpl = $daoImpl;
    }

    //some other functions that are common to concrete models
}

/*
* a concrete model
*/
class Model extends AbstractModel {

    function findAll($params) {
        //You can use the $daoImpl of AbstractModel to perform a CRUD operation
        $this->daoImpl->findAll($params);
    }

}

现在,每当您实例化一个具体模型时,您都会将 DAO 实现注入到模型类中。

//inject a DAOInterface implementation into Model
$model = new Model(new DAOImpl());
$model->findAll($params);

这里的优点是您可以在测试期间存根不同的 DAO 实现,也许这就是 DI 容器派上用场的地方。几天前我在创建 DI 容器时创建了一个类似的代码示例。

顺便说一句,我认为不需要将$container对象放入 AbstractDAO 中,为什么不传入调用容器属性时返回的对象。这样,您可以使用类型提示来强制对象参数为某种类型,并在传入错误的对象时鼓励采用快速失败机制,并且您可能还会发现创建一个 Config 类来处理您的$driver详细信息是有益的,这样用户可以自由配置他们想要用于 db 的驱动程序。

于 2013-03-04T08:50:31.130 回答