PHP - слой абстракции БД использует статический класс против одноэлементного объекта? - PullRequest
24 голосов
/ 15 мая 2010

Я не хочу создавать дискуссию о синглтоне лучше, чем статический или лучше, чем глобальный, и т. Д. Я прочитал десятки вопросов о подобных предметах на SO, но я не смог придумать ответ на этот конкретный вопрос поэтому я надеюсь, что теперь кто-то сможет осветить меня, ответив на этот вопрос одним (или более) действительно простыми ПРИМЕРАМИ , а не просто теоретическими дискуссиями.

В моем приложении у меня есть типичный класс БД для абстрагирования слоя БД и для выполнения задач на БД без необходимости писать везде в коде mysql_connect / mysql_select_db / mysql...

Я мог бы написать класс либо как СТАТИЧЕСКИЙ КЛАСС:

class DB
{
   private static $connection = FALSE; //connection to be opened

   //DB connection values
   private static $server = NULL; private static $usr = NULL; private static $psw = NULL; private static $name = NULL;

   public static function init($db_server, $db_usr, $db_psw, $db_name)
   {
      //simply stores connections values, without opening connection
   }

   public static function query($query_string)
   {
      //performs query over alerady opened connection, if not open, it opens connection 1st
   }

   ...
}

ИЛИ как SINGLETON:

class DBSingleton
{
   private $inst = NULL;
   private $connection = FALSE; //connection to be opened

   //DB connection values
   private $server = NULL; private $usr = NULL; private $psw = NULL; private $name = NULL;

   public static function getInstance($db_server, $db_usr, $db_psw, $db_name)
   {
      //simply stores connections values, without opening connection

      if($inst === NULL)
         $this->inst = new DBSingleton();
      return $this->inst;
   }
   private __construct()...

   public function query($query_string)
   {
      //performs query over already opened connection, if connection is not open, it opens connection 1st
   }

   ...
}

Затем в моем приложении, если я захочу запросить базу данных, я мог бы сделать

//Performing query using static DB object
DB:init(HOST, USR, PSW, DB_NAME);
DB::query("SELECT...");

//Performing query using DB singleton
$temp = DBSingleton::getInstance(HOST, USR, PSW, DB_NAME);
$temp->query("SELECT...");

Для меня Синглтон получил единственное преимущество, чтобы не объявлять static каждый метод класса. Я уверен, что некоторые из вас могут дать мне ПРИМЕР реального преимущества синглтона в этом конкретном случае . Заранее спасибо.

Ответы [ 5 ]

7 голосов
/ 09 апреля 2011

Что не так со следующим (упрощенным) примером:

class Database
{
    protected $_connection;

    protected $_config;

    public function __construct( array $config ) // or other means of passing config vars
    {
        $this->_config = $config;
    }

    public function query( $query )
    {
        // use lazy loading getter
        return $this->_getConnection()->query( $query );
    }

    protected function _getConnection()
    {
        // lazy load connection
        if( $this->_connection === null )
        {
            $dsn = /* create valid dsn string from $this->_config */;

            try
            {
                $this->_connection = new PDO( $dsn, $this->_config[ 'username' ], $this->_config[ 'password' ] );
            }
            catch( PDOException $e )
            {
                /* handle failed connecting */
            }
        }

        return $this->_connection;
    }
}

$db1 = new Database( array(
    'driver'   => 'mysql',
    'host'     => 'localhost',
    'dbname'   => 'test',
    'username' => 'test_root',
    'password' => '**********'
) );

$db2 = new Database( array(
    'driver'   => 'pgsql',
    'host'     => '213.222.1.43',
    'dbname'   => 'otherdb',
    'username' => 'otherdb_root',
    'password' => '**********'
) );

$someModel       = new SomeModel( $db1 );
$someOtherModel  = new SomeOtherModel( $db2 );
$yetAnotherModel = new YetAnotherModel( $db2 );

Это демонстрирует, как вы можете использовать ленивые загрузочные соединения, и при этом иметь гибкость в использовании разных соединений с базой данных.

Экземпляры базы данных будут подключаться к своему индивидуальному соединению только тогда, когда объект, который использует один из экземпляров (в данном случае одна из моделей), решит вызвать метод экземпляра.

4 голосов
/ 15 апреля 2011

Мой совет: ПРЕКРАТИТЕ использовать Singleton и static все вместе.

Почему? Потому что вы будете вставлять зависимости, которые сделают ваш код непригодным для использования в других проектах, и не позволят его тестировать. Также забудьте о слабой связи при использовании синглтона.

Альтернативы? Внедрение зависимости. http://www.potstuck.com/2009/01/08/php-dependency-injection

4 голосов
/ 15 мая 2010

В моем последнем проекте я фактически пошел против "хороших" принципов проектирования, сделав класс базы данных полностью статичным.Причиной этого является то, что я использовал много кеширования на объектах PHP.Первоначально я передавал базу данных через конструктор каждого объекта как инъекцию зависимостей, однако я хотел убедиться, что база данных не должна соединяться, если это не является абсолютно необходимым.Таким образом, использование базы данных в качестве переменной-члена этого объекта было бы нецелесообразным, поскольку, если вы не сериализовали объект из кэша, вы не захотите подключаться к базе данных, если вы фактически не выполнили над ней операцию.

Таким образом, в конце концов у меня было только две (публичные) статические функции, Database :: fetch () и Database :: execute (), которые проверяли, подключено ли это уже или нет, а если нет, то подключались и выполнялизапрос.Таким образом, мне не придется беспокоиться о десериализации и я буду подключаться как можно реже.Технически это делает невозможным юнит-тестирование.

Вам не всегда нужно следовать каждой хорошей практике.Но я все равно рекомендовал бы не делать то, что сделал, поскольку некоторые считают это преждевременной оптимизацией.

1 голос
/ 19 апреля 2012

Создание статической библиотеки БД, безусловно, короче и быстрее, чем выполнение:

$db = DBSingleton::blabla(); // everytime I need ya

Но также, поскольку это глобально, заманчиво использовать везде.

Итак, выберите другие методы, если вы хотите очистить код ... и выберите статический, если вам нужен быстрый код;

0 голосов
/ 22 августа 2015
/* Data base*/
 class Database
{
    /* Database field definition */
    private static $_instance; /instance
    private $_connection;
    private $DB_USER = "database_user_name_here";
    private $DB_PASS = "your_password_here";
    private $DB_NAME = "your_database_name_here";
    private $DB_SERVER = "localhost";

    /* Initiate the database connection */
    private function __construct()
    {
        $this->_connection = new mysqli($this->DB_SERVER ,
                                        $this->DB_USER ,
                                        $this->DB_PASS ,
                                        $this->DB_NAME);
        /* Test if connection succeeded */
        if (mysqli_connect_errno()) {
            die("Database connection failed: " .
                mysqli_connect_error() .
                " (" . mysqli_connect_errno() . ")"
            );
        }
    }

    /**
     * Instance of the database
     * @return Database
     *
     */
    public static function Instance()
    {
        if (!self::$_instance) { // If no instance then make one
            self::$_instance = new self();
        }

        return self::$_instance;
    }

    /**
     * Void duplicate connection
     */
    private function __clone() { }

    /* Return a connection */
    public function getConnection()
    {
        return $this->_connection;
    }

}

/** This is how you would use it in a different class.
  @var TYPE_NAME $connection */
$db = Database::Instance();
$connection = $db->getConnection();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...