【问题标题】:organizing PHP OOP classes to work with DB组织 PHP OOP 类以使用 DB
【发布时间】:2013-08-05 04:34:42
【问题描述】:

今天我有一个类结构非常奇怪的 PHP 项目。像这样:

db_base
  `-- db_base_ext extends db_base
        +-- module_1 extends db_base_ext
        +-- module_2 extends db_base_ext
        .   ...
        +-- module_N extends db_base_ext
        `-- db_user extends db_base_ext

class_1
  `-- submodule_1_1 extends class_1

db_base 连接到__construct() 中的db,并有一些辅助方法。

执行示例:

$db = new db_user();
$user = new user($db);
unset($db);

$db = new module_2();

我不喜欢在这段代码中我们两次连接到 DB 并且整个类结构不是很好。

我怎样才能让它变得更好?我的意思是只创建一个数据库实例,然后使用任何类(对应于数据库)?

单例模式对此有好处吗?

【问题讨论】:

标签: php database oop


【解决方案1】:

你的问题是,你的结构,

module_2 extends db_base_ext
db_user extends db_base_ext
submodule_1_1 extends class_1

它们都破坏了Single Responsibility PrincipleLiskov Substitution Principle

我怎样才能让它变得更好。我的意思是只创建一个数据库实例,然后 可以与任何类(对应于 DB)一起工作吗?

Dependency injection 是要走的路。您将实例化一个数据库实例,您的所有类将共享相同 $db 实例。

final class MySQLPDO extends PDO
{
    public function __construct(array $params)
    {
      parent::__construct(sprintf('mysql: host=%s; dbname=%s', $params['host'], $params['database']), $params['username'], $params['password']);

      $this->setAttribute(parent::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES UTF8');
      $this->setAttribute(parent::ATTR_ERRMODE, parent::ERRMODE_EXCEPTION);
      $this->setAttribute(parent::ATTR_EMULATE_PREPARES, false);
      $this->setAttribute(parent::ATTR_DEFAULT_FETCH_MODE, parent::FETCH_ASSOC);

    }
}

$db = new MySQLPDO(array(
     'host'     => 'localhost',
     'database' => 'foo',
     'username' => 'root',
     'password' => '',
));
$user = new User($db);

$module = new Module1($user);
$foo = new Foo($db);

那么,你会在这里获得什么?重用能力和测试能力。


注意,您应该避免使用 Singleton,因为它们会引入另一种形式的全局状态,这不利于单元测试。

【讨论】:

  • 我不确定您的提案的一个实例与单例实现有何不同。你能解释一下区别吗?我很感兴趣
  • 我希望你的意思是$module = new Module1($db); ?
  • @zewa666 唯一的区别是,您可以像$db = DB::getInstance() 一样称呼您的单身人士。由于您将其称为DB::getInstance(),因此您引入了另一种形式的全局状态。应该使用DI 而不是Singletons...
  • 不要命名这个创建类适配器。它所做的只是将 params 数组(魔术键、未记录的用法、坏主意)转换为新的 PDO 配置。一个函数可以做到这一点,只需提供配置。但实际上我只是想评论这个名字:) 所以适配器作为名字是一种气味,你能想象一个更好的名字吗?
  • 如果你从 PDO 扩展,它不是PDOProvider,它只是一个PDO,例如MyPDO。当您调用名为 provide() 或类似的方法时,提供程序将返回一个 PDO。至少我是这么理解的。 - 关闭只是示例性的,而且,PHP 5.3 是生命的终结。如果您提出建议,请在 SO 上将其保留为适当的当前稳定 PHP 版本。这不是 2008 年提出的问题(并不是说关闭是解决问题的方法)。
【解决方案2】:

使用正确的类名

首先我注意到项目中的类被命名为函数。如果项目尝试使用 PEAR 命名约定,它应该正确。

使模块独立于数据库类

您应该断开模块与db_base_ext 类的连接。如果模块需要访问数据库实例,可以使用构造函数参数或设置器来提供。它不应该直接扩展类。

$module = new Module($database);

【讨论】:

  • 您确定将所有模块与db_base_ext 断开连接是个好主意吗?为什么有必要?在 codeigniter for exaple 中,所有模型都扩展了基本类 CI_Model
  • @Yekver 我绝对确定 :) 问问自己这个问题:“模块是一种数据库吗?”。一个模块显然不是。所以它不应该试图表现得像一种数据库或它的扩展。
  • @Yekver 调查 CI 到底做了什么,并做相反的事情,你应该没问题。
【解决方案3】:

您应该考虑为您的 DB 类使用单例模式。

Take a look here for a tutorial。 它使用 PDO 进行通信,但对 mysqli 或其他类型也应有效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多