Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Singleton thay thế cho PHP PDO

Việc sử dụng singleton-pattern (hoặc antipattern) được coi là phương pháp không tốt vì nó làm cho việc kiểm tra mã của bạn trở nên rất khó khăn và sự thiếu hụt rất phức tạp cho đến khi dự án trở nên khó quản lý vào một thời điểm nào đó. Bạn chỉ có thể có một phiên bản cố định của đối tượng trên mỗi php-process. Khi viết các bài kiểm tra đơn vị tự động cho mã của bạn, bạn cần có thể thay thế đối tượng mà mã bạn muốn kiểm tra sử dụng bằng một bài kiểm tra kép hoạt động theo cách có thể khắc phục được. Khi mã bạn muốn kiểm tra sử dụng một mã đơn, thì bạn không thể thay thế mã đó bằng mã kiểm tra kép.

Cách tốt nhất (theo hiểu biết của tôi) để tổ chức tương tác giữa các đối tượng (như Đối tượng-Cơ sở dữ liệu của bạn và các đối tượng khác sử dụng cơ sở dữ liệu) sẽ là đảo ngược hướng của các phần bổ sung. Điều đó có nghĩa là mã của bạn không yêu cầu đối tượng mà nó cần từ một nguồn bên ngoài (trong hầu hết các trường hợp là một đối tượng toàn cục như phương thức tĩnh 'get_instance' từ mã của bạn) mà thay vào đó, đối tượng depency của nó (đối tượng mà nó cần) được cung cấp từ bên ngoài trước khi nó cần nó. Thông thường, bạn sẽ sử dụng Trình quản lý độ sâu-tiêm / Vùng chứa như cái này một từ dự án symfony để soạn các đối tượng của bạn.

Các đối tượng sử dụng đối tượng cơ sở dữ liệu sẽ được đưa vào khi xây dựng. Nó có thể được đưa vào bằng phương thức setter hoặc trong phương thức khởi tạo. Trong hầu hết các trường hợp (không phải tất cả) thì tốt hơn là nên đưa độ sâu (đối tượng db của bạn) vào hàm tạo vì theo cách đó đối tượng sử dụng đối tượng db sẽ không bao giờ ở trạng thái không hợp lệ.

Ví dụ:

interface DatabaseInterface
{
    function query($statement, array $parameters = array());
}

interface UserLoaderInterface
{
    public function loadUser($userId);
}

class DB extends PDO implements DatabaseInterface
{
    function __construct(
        $dsn = 'mysql:host=localhost;dbname=kida',
        $username = 'root',
        $password = 'root',
    ) {
        try {
            parent::__construct($dsn, $username, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'");
            parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch(PDOException $e) {
            echo $e->getMessage();
        }
    }

    function query($statement, array $parameters = array())
    {
        # ...
    }
}

class SomeFileBasedDB implements DatabaseInterface
{
    function __construct($filepath)
    {
        # ...
    }

    function query($statement, array $parameters = array())
    {
        # ...
    }
}

class UserLoader implements UserLoaderInterface
{
    protected $db;

    public function __construct(DatabaseInterface $db)
    {
        $this->db = $db;
    }

    public function loadUser($userId)
    {
        $row = $this->db->query("SELECT name, email FROM users WHERE id=?", [$userId]);

        $user = new User();
        $user->setName($row[0]);
        $user->setEmail($row[1]);

        return $user;
    }
}

# the following would be replaced by whatever DI software you use,
# but a simple array can show the concept.


# load this from a config file
$parameters = array();
$parameters['dsn'] = "mysql:host=my_db_server.com;dbname=kida_production";
$parameters['db_user'] = "mydbuser";
$parameters['db_pass'] = "mydbpassword";
$parameters['file_db_path'] = "/some/path/to/file.db";


# this will be set up in a seperate file to define how the objects are composed
# (in symfony, these are called 'services' and this would be defined in a 'services.xml' file)
$container = array();
$container['db'] = new DB($parameters['dsn'], $parameters['db_user'], $parameters['db_pass']);
$container['fileDb'] = new SomeFileBasedDB($parameters['file_db_path']);

# the same class (UserLoader) can now load it's users from different sources without having to know about it.
$container['userLoader'] = new UserLoader($container['db']);
# or: $container['userLoader'] = new UserLoader($container['fileDb']);

# you can easily change the behaviour of your objects by wrapping them into proxy objects.
# (In symfony this is called 'decorator-pattern')
$container['userLoader'] = new SomeUserLoaderProxy($container['userLoader'], $container['db']);

# here you can choose which user-loader is used by the user-controller
$container['userController'] = new UserController($container['fileUserLoader'], $container['viewRenderer']);

Lưu ý rằng các lớp khác nhau không biết về nhau như thế nào. Không có sự suy giảm trực tiếp giữa chúng. Điều này được thực hiện bằng cách không yêu cầu lớp thực tế trong phương thức khởi tạo, mà thay vào đó, yêu cầu giao diện cung cấp các phương thức mà nó cần.

Bằng cách đó, bạn luôn có thể viết các thay thế cho các lớp của mình và chỉ cần thay thế chúng trong vùng chứa depency-injection. Bạn không cần phải kiểm tra toàn bộ codebase vì phần thay thế chỉ cần triển khai cùng một giao diện được sử dụng bởi tất cả các lớp khác. Bạn biết rằng mọi thứ sẽ tiếp tục hoạt động vì mọi thành phần sử dụng lớp cũ chỉ biết về giao diện và chỉ gọi các phương thức được giao diện biết.

P.S:Xin thứ lỗi vì những tham chiếu liên tục của tôi đến dự án symfony, đó chỉ là thứ tôi quen dùng nhất. Các dự án khác như Drupal, Propel hoặc Zend có lẽ cũng có các khái niệm như thế này.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SonarQube:Làm cách nào để giảm kích thước của Measure_data.ibd?

  2. MySql trong DNX 5.0

  3. NHÓM THEO CÓ HAVING (DISTINCT):PHP, MYSQL

  4. MySQL SELECT DISTINCT phải phân biệt chữ hoa chữ thường?

  5. Cách nhanh nhất để thăm dò một bảng MySQL cho các hàng mới là gì?