我正在用PHP教我自己的OOP。
我正在创建几个小型网络应用程序并且已经遵循了很多教程,这些教程要么通过Singleton创建数据库(使用PDO),要么通过传递全局。我已经读到这些几乎是同一件事,并且要像瘟疫那样被避免。
因此,我观看了Google Tech会谈清洁代码,并阅读了几乎所有关于依赖注入等的SO文章。我有几个问题。
例如,在尝试符合单个repsonibility类时,我创建了三个。
请注意,我尝试创建单个实例,而不创建Singleton模式。
所以我尝试将每个类的依赖项传递给链。我发现自己处于一个需要创建所有对象的位置(从DB向下),因此我可以注入依赖项。出于某种原因,我认为它会以另一种方式起作用,我会创建第一个对象,这将为我创造第二个对象等。我明显错过了什么?
希望这也有助于其他人 - 似乎有很多关于这些东西和数据库的问题,但很少有很好的例子。
(我应该提到这确实有效,我确实从数据库中获得了酒店名称列表!)
TestCode.php
include './classes/DB.php';
include './classes/DBFactory.php';
include './classes/DBInstance.php';
include './classes/Location.php';
$db = new DB;
$dbfactory = new DBFactory($db);
$dbinstance = new DBInstance($dbfactory);
$dbh = $dbinstance->getDbInstance();
//Example business logic
$location_names = Location::getLocationNames($dbh);
print_r($location_names);
Class DB.php:
class DB {
private $_dbhost = 'myhost';
private $_dbname = 'myname';
private $_dbuser = 'myuser';
private $_dbpass = 'mypass';
private $_error;
public function connect() {
try {
return new PDO("mysql:host=$this->_dbhost;dbname=$this->_dbname",
$this->_dbuser, $this->_dbpass);
}
catch (PDOException $e) {
$this->_error = 'Error! ' . $e->getMessage() . '<br />';
die();
}
}
public function getError() {
if (isset($this->_error)) {
return $this->_error;
}
}
}
Class DBFactory.php
class DBFactory {
private $_dbh;
public function __construct(DB $db) {
$this->_dbh = $db;
}
public function Create() {
return $this->_dbh->Connect();
}
}
Class DBInstance.php
class DBInstance {
private static $_dbinstance;
public function __construct(DBFactory $dbfactory) {
if (!isset(self::$_dbinstance)) {
self::$_dbinstance = $dbfactory->Create();
}
}
public function getDbInstance() {
return self::$_dbinstance;
}
}
答案 0 :(得分:1)
KISS
不要让事情变得比他们复杂,当然这只是我的意见,但正如我所看到的那样,你正在构建一个复杂的解决方案来解决其他人说可能存在的问题。 Php不是多线程的,因此有一个最大的争论是落水。 (在极少数情况下可能会这样)
我现在使用单例作为我的数据库连接大约15年了,从来没有遇到任何问题,我确实玩了不同的连接,只有一个单例处理几个连接实例,但无论如何......它很有用每个看着代码的人都直接理解它。 我没有使用全局变量,因为它们可以被覆盖并且很难预测(当它保存正确的对象时,以及何时/为什么它们没有)
使用OOP使您的代码更清晰,更易于使用且更灵活。 不要用它来修复那些不存在的问题,并使你的代码更复杂,因为其他人告诉你。
处理多个不同连接的db-connection单例类的一个非常简单的例子。
class singleton{
private static $_instances=array();
public static function getInstance($connectionName){
if(!isset(self::$_instance[$connectionName]){
self::$_instance[$connectionName]=self::_getConnection($connectionName);
}
return self::$_instance[$connectionName];
}
}
只是我的2美分
答案 1 :(得分:1)
如果你有单身人士,为什么你有工厂?这是不必要的。
这是一场永无休止的辩论,但我主张不要将单例用于数据库连接。
就大多数应用程序而言,您只有一个数据通道,您可以认为您的数据库连接是唯一的,但这可能并非总是如此。
实际上,创建单例数据库连接所做的努力甚至比创建常规数据库更大。
此外,您的班级DB
不可配置,因此,您需要在连接参数更改时更改它。我认为DB
是一个非常糟糕的名字。
我宁愿调用此Storage
并执行以下操作:
inteface Storage {
public function insert($container, array $data);
public function update($container, array $data, $where);
public function delete($container, $where);
public function getAll($container);
public function getOne($identifier);
}
final class PdoStorage implements Storage {
private $dbh;
private $dsn;
private $user;
private $pswd;
public function __construct($dsn, $user, $pswd) {
$this->dsn = $dsn;
$this->user = $user;
$this->pswd = $pswd;
}
// Lazy Initialization
private function connect() {
if ($this->dbh === null)
$this->dbh = new PDO($this->dsn, $this->user, $this->pswd);
}
public function insert($container, array $data) {
$this->connect();
// ... omitted for brevity
}
}
现在,当您需要数据库存储时,您可以:
$someObj = new SomeClass(new PdoStorage(...));
现在您可能想知道是否需要为每个依赖于它的单个对象创建PdoStorage
。
答案是:没有!
现在您可以使用工厂来简化您的生活。
class SomeFactory {
private $defaultStorage;
public function __construct(Storage $storage) {
$this->defaultStorage = $storage;
}
public function create($type) {
// Somehow fetches the correct class to instantiate and put it into $class variable , for example... and then
return new $class($this->defaultStorage); // Or you'd better do this with reflection
}
}
$factory = new SomeFactory(new PdoStorage(...));
$factory->create('SomeClass');
这样,如果需要,您可以只使用一个或多个数据库连接器。
答案 2 :(得分:1)
您的代码似乎按照您的意愿执行..但也许我们可以使用继承来减少对象实例化,也许我们可以避免在实例化类中使用静态属性。
此外,关于使用能够处理多个连接的依赖注入模式,但支持使用它的单个实例。
之后的第一个例子$params = array
('host'=>'localhost',
'db'=>'ice',
'user'=>'kopitar',
'pass'=>'topnet',
'charset'=>'utf8'); // passing the charset explicitely is great
$handle = new handle($params);
$db = $handle->getInstance();
我们可以将$db
传递给我们的函数
$location_names = Location::getLocationNames($db);
或整个$句柄。只要$ handle没有重建,它将始终返回相同的数据库连接。
$location_names = Location::getLocationNames($handle);
如果我想重建,我需要整个$handle
$handle->__construct(/* params but with another database infos */);
$db2 = $handle->getInstance();
对于课程,我认为我们希望params从instanciated类到达,所以我们可以稍后更改它们。
class db {
function __construct($params) {
foreach ($params as $param => $value) {
$this->{$param} = $value; // assigns the connections infos
}
}
protected function connect() {
$dsn = 'mysql:host='.$this->host.';dbname='.$this->db.';charset='.$this->charset;
return new PDO($dsn,$this->user,$this->pass);
}
}
工厂从params创建一个连接并将其传递给其他东西,好工厂
class factory extends db {
protected function create() {
return $this->connect();
}
}
现在我们想让我们的对象保持连接,只要我们不重建它。所以我们把它给实例
class instance extends factory {
function instantiate() {
$this->instance = $this->create();
}
}
最后但并非最不重要的是,我们的句柄返回实例。它可以在实例类中.....................
但我觉得有四个人并没有找到真正的理由不这样做。
class handle extends instance {
function __construct($params) {
db::__construct($params);
$this->instantiate(); // when we construct a handle, we assign an instance to the instance property
}
function getInstance() {
return $this->instance;
}
}