【问题标题】:Reuse Database Connection When Creating PHP Objects创建 PHP 对象时重用数据库连接
【发布时间】:2013-12-29 21:16:27
【问题描述】:

在 PHP 中,我有两个类:DatabaseItem

数据库包含连接属性和方法。比如Database::Query可以用来传递查询字符串等

Item 是一个通用的项目标识符。它是通过传递一个 itemID 来构建的,然后用于查询数据库以获取其余的项目信息。

在这种情况下,如果需要数据库访问权限,创建 Item 对象的最佳做法是什么?使用这种语法创建每个是否正常:

$item = new Item(12345, $db);

或者创建 Database 对象并将其用于应用程序中创建的每个 Item 是否更好、可接受或可能,这样调用可以变成:

$item = new Item(12345);

第二个似乎更简洁(并且可以扩展,以便类似类型的对象也不需要 $db 插件),但我正在寻找比我更有经验的人的建议!谢谢!

【问题讨论】:

  • 您可以使用this approach 对数据库实例进行延迟初始化(也可以针对mysqli 进行调整)。

标签: php mysql sql database oop


【解决方案1】:

通常,数据库连接对象是全局的,或可全局访问的。这适用于绝大多数应用程序。

我做了这样的事情(为了举例而简化):

$db = connect_to_db();

function GetDB()
{
   global $db;
   return $db
}

//inside the item object

function Load( $id)
{    
   $db = GetDB();
   $db->query(..);
}

当然,在某些情况下这不是最佳路线。与往常一样,这取决于您的应用程序的具体需求。

【讨论】:

  • -1:通常在任何应用程序中都是最有害的。
  • 没有。不要使用全局声明。
  • 哦,拜托,对于绝大多数应用程序来说,这都很好。
  • @GrandmasterB 当然它“有效”,但没有人应该将其作为解决任何编程问题的建议方法。您需要一个类来理解全局范围内存在的某些特定变量名称。这严格违反了封装的 OOP 原则。另外,当您更改该变量名称时会发生什么?突然之间,你必须去改变一堆类来更新这个变量名。您将您的类与没有业务理解的应用程序细节紧密耦合。
  • 我相信你误读了这个例子。 Load() 方法中的 $db 是本地的。示例中的 GetDB() 是用于返回连接对象实例的全局可用方法或函数的占位符。本地变量与名称无关。该对象唯一需要注意的是用于返回对数据库连接的引用的方法的名称。这比需要传递对数据库连接的引用来创建系统中使用的每个对象要简单得多。
【解决方案2】:

我建议您对数据库连接使用单例模式。这实际上是最佳实践。因为你真的不需要数据库连接的实例。

class Database_Instance
{
    private static $database;
    public static function getDatabaseObject() {
        if (!self::$db) {
            self::$db = new PDO( );
            }
        return self::$db;
    }
}

function callWhatSoEver()
{
    $connection = Database_Instance::getDatabaseObject();

}

有关单例模式的更多信息,请参阅:http://en.wikipedia.org/wiki/Singleton_pattern

【讨论】:

  • 根本不是最佳实践。也许是 10 年前的事了。
  • 我相信依赖注入几乎是行业发展的方式。这在 PHP wspace 中可能不那么明显,因为 PHP 并没有被真正的大型软件公司广泛使用。
  • 谢谢,不胜感激;)
【解决方案3】:

我建议大多数经验丰富的开发人员倾向于使用依赖注入的方法,如您的第一个示例所示。

为什么?

这很大程度上是因为这允许您将依赖项注入到的类与依赖项的实现分离。

所以考虑这个依赖注入示例:

Class some_class {
    protected $db;
    __construct($db) {
        if($db instanceof some_db_class === false) {
            throw new Exception('Improper parameter passed.');
        }
        $this->db = $db;
    } 

}

在这里,您可以传递任何类型的对象,只要它是some_db_class 的实例,它可以是该对象的子类,它实现了该类使用的相同方法。只要实现了方法,这对此类无关紧要(当然,除了检查其实例类型之外,您还可以检查传递的对象是否实现了特定接口)。

这意味着,例如,您可以传递一个模拟 DB 对象进行测试或类似的东西。只要方法被实现,类就不管了。

现在考虑单例方法(或从类中类似的 DB 实例化):

Class some_class {
    protected $db;
    __construct() {
        $this->db = some_db_class::get_instance();
    } 
}

在这里,您已将您的类与特定的数据库类紧密耦合。如果您想使用模拟 DB 实现来测试这个类,那么您需要修改该类来这样做会变得非常痛苦。

我什至不会讨论使用global,因为这只是一种糟糕的做法,根本不应该考虑。

【讨论】:

  • 我建议只对 some_db_class 进行类型提示,但如果您要这样做,则有一个例外:InvalidArgumentException ;)
  • @jimbo 是的,类型提示是一个很好的建议,但如果您需要检查传递的对象是否实现了特定接口,则没有帮助。
  • 您当然可以为界面输入提示...这是最好的处理方式:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-28
  • 1970-01-01
  • 2015-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-09
相关资源
最近更新 更多