Global hoặc Singleton cho kết nối cơ sở dữ liệu?


81

Lợi ích của việc sử dụng singleton thay vì global cho các kết nối cơ sở dữ liệu trong PHP là gì? Tôi cảm thấy việc sử dụng singleton thay vì global làm cho mã phức tạp một cách không cần thiết.

Mã với Toàn cầu

$conn = new PDO(...);

function getSomething()
{
    global $conn;
    .
    .
    .
}

Mã với Singleton

class DB_Instance
{
    private static $db;

    public static function getDBO()
    {
        if (!self::$db)
            self::$db = new PDO(...);

        return self::$db;
    }
}

function getSomething()
{
    $conn = DB_Instance::getDBO();
    .
    .
    .
}

Nếu có cách tốt hơn để khởi tạo kết nối cơ sở dữ liệu khác với global hoặc singleton, vui lòng đề cập đến nó và mô tả những ưu điểm của nó so với global hoặc singleton.


Nếu bạn đang lên kế hoạch sử dụng PDO tại các trình xử lý phiên tùy chỉnh, bạn nên biết một số đặc điểm: stackoverflow.com/questions/2595860/pdo-prepare-silently-fails
Wabbitseason,

Câu trả lời:


105

Tôi biết điều này đã cũ, nhưng câu trả lời của Dr8k gần như đã có.

Khi bạn đang cân nhắc viết một đoạn mã, hãy giả sử rằng nó sẽ thay đổi. Điều đó không có nghĩa là bạn đang giả định các loại thay đổi mà nó sẽ tồn tại vào một thời điểm nào đó trong tương lai, mà là một số hình thức thay đổi sẽ được thực hiện.

Biến nó thành một mục tiêu để giảm thiểu nỗi đau của việc thực hiện thay đổi trong tương lai: một toàn cầu rất nguy hiểm vì rất khó để quản lý ở một vị trí duy nhất. Điều gì xảy ra nếu tôi muốn làm cho bối cảnh kết nối cơ sở dữ liệu đó nhận biết được trong tương lai? Điều gì sẽ xảy ra nếu tôi muốn nó đóng và tự mở lại sau mỗi lần thứ 5 nó được sử dụng. Điều gì sẽ xảy ra nếu tôi quyết định rằng vì lợi ích của việc mở rộng ứng dụng của mình, tôi muốn sử dụng nhóm 10 kết nối? Hoặc số lượng kết nối có thể định cấu hình?

Một nhà máy singleton cung cấp cho bạn sự linh hoạt đó. Tôi thiết lập nó với rất ít phức tạp và đạt được nhiều hơn là chỉ truy cập vào cùng một kết nối; Tôi có khả năng thay đổi cách kết nối đó được truyền cho tôi sau này một cách đơn giản.

Lưu ý rằng tôi nói nhà máy singleton trái ngược với chỉ đơn giản là singleton . Có một sự khác biệt nhỏ quý giá giữa singleton và global, đúng. Và vì điều đó, không có lý do gì để có một kết nối singleton: tại sao bạn lại dành thời gian thiết lập điều đó trong khi bạn có thể tạo một toàn cầu thông thường?

Những gì một nhà máy giúp bạn có được là lý do tại sao để có được kết nối và một vị trí riêng biệt để quyết định những kết nối (hoặc kết nối) nào bạn sẽ nhận được.

Thí dụ

class ConnectionFactory
{
    private static $factory;
    private $db;

    public static function getFactory()
    {
        if (!self::$factory)
            self::$factory = new ConnectionFactory(...);
        return self::$factory;
    }

    public function getConnection() {
        if (!$this->db)
            $this->db = new PDO(...);
        return $this->db;
    }
}

function getSomething()
{
    $conn = ConnectionFactory::getFactory()->getConnection();
    .
    .
    .
}

Sau đó, trong 6 tháng khi ứng dụng của bạn trở nên cực kỳ nổi tiếng và nhận được nhiều sự chú ý và đánh giá cao và bạn quyết định rằng mình cần nhiều hơn một kết nối duy nhất, tất cả những gì bạn phải làm là triển khai một số tổng hợp trong phương thức getConnection (). Hoặc nếu bạn quyết định rằng bạn muốn một trình bao bọc thực hiện ghi nhật ký SQL, bạn có thể chuyển một lớp con PDO. Hoặc nếu bạn quyết định muốn có một kết nối mới trên mỗi lần gọi, bạn có thể làm điều đó. Nó linh hoạt, thay vì cứng nhắc.

16 dòng mã, bao gồm cả dấu ngoặc nhọn, sẽ giúp bạn tiết kiệm hàng giờ đồng hồ và hàng giờ để cấu trúc lại thành một thứ gì đó tương tự một cách kỳ lạ.

Lưu ý rằng tôi không coi đây là "Tính năng Creep" vì tôi không thực hiện bất kỳ triển khai tính năng nào trong vòng đầu tiên. Đó là đường biên giới "Future Creep", nhưng tại một số điểm, ý tưởng "viết mã cho ngày mai hôm nay" luôn là một điều tồi tệ đối với tôi.


3
Không chắc lắm, nhưng tôi nghĩ ý bạn là: public function getConnection () {if (! $ This-> db) $ this-> db = new PDO (...); return $ this-> db; }
Dycey

Cảm ơn! Tôi có mất bất kỳ lợi ích nào của việc sử dụng phương pháp này bằng cách sử dụng return self::$factory->getConnection();thay thế return self::$factory;không?
Nico Burns

3
Tôi muốn sử dụng mã này trong một dự án mà tôi đang làm. Tôi có thể trích dẫn trang này không, và nếu có, văn bản này thuộc giấy phép nào? Nó có phải là CC-BY, BSD hay thứ gì khác không? Tôi hiện đã tuyên bố điều này là "Không xác định - tin rằng Miền công cộng" nhưng tôi muốn ghi các điều kiện cấp phép chính xác trên đó.
JonTheNiceGuy

2
Tôi nghĩ chúng ta nên tạo "$ db" "$ this-> db" trong phương thức getConnection (), nếu không biến "private $ db" "không được sử dụng", không được tham chiếu chính thức ở bất kỳ đâu.
nhà phát triển 10

Xin chào, giải pháp của bạn trông rất tuyệt và có thể mở rộng. Vui lòng cho tôi biết những gì cần phải được thực hiện tại đây self :: $ factory = new ConnectionFactory (...);
Ananda

16

Tôi không chắc mình có thể trả lời câu hỏi cụ thể của bạn, nhưng muốn gợi ý rằng các đối tượng kết nối global / singleton có thể không phải là ý tưởng tốt nhất nếu điều này xảy ra đối với một hệ thống dựa trên web. DBMS thường được thiết kế để quản lý số lượng lớn các kết nối duy nhất một cách hiệu quả. Nếu bạn đang sử dụng một đối tượng kết nối toàn cục, thì bạn đang thực hiện một số việc:

  1. Buộc các trang của bạn thực hiện tất cả các kết nối cơ sở dữ liệu một cách tuần tự và giết chết mọi nỗ lực tải trang không đồng bộ.

  2. Có khả năng giữ khóa mở trên các phần tử cơ sở dữ liệu lâu hơn mức cần thiết, làm chậm hiệu suất cơ sở dữ liệu tổng thể.

  3. Tối đa hóa tổng số kết nối đồng thời mà cơ sở dữ liệu của bạn có thể hỗ trợ và chặn người dùng mới truy cập tài nguyên.

Tôi chắc chắn rằng có những hậu quả tiềm ẩn khác. Hãy nhớ rằng, phương pháp này sẽ cố gắng duy trì kết nối cơ sở dữ liệu cho mọi người dùng truy cập trang web. Nếu bạn chỉ có một hoặc hai người dùng, không thành vấn đề. Nếu đây là một trang web công cộng và bạn muốn có lượng truy cập thì khả năng mở rộng sẽ trở thành một vấn đề.

[BIÊN TẬP]

Trong các tình huống có quy mô lớn hơn, việc tạo các kết nối mới mỗi khi bạn chạm vào cơ sở dữ liệu có thể không tốt. Tuy nhiên, câu trả lời không phải là tạo kết nối toàn cầu và sử dụng lại nó cho mọi thứ. Câu trả lời là kết nối tổng hợp.

Với tính năng gộp kết nối, một số kết nối riêng biệt được duy trì. Khi ứng dụng yêu cầu kết nối, kết nối khả dụng đầu tiên từ nhóm sẽ được truy xuất và sau đó trở lại nhóm khi công việc của nó được thực hiện. Nếu một kết nối được yêu cầu và không có kết nối nào khả dụng, một trong hai điều sẽ xảy ra: a) nếu không đạt đến số lượng kết nối tối đa cho phép, một kết nối mới sẽ được mở hoặc b) ứng dụng buộc phải đợi kết nối khả dụng .

Lưu ý: Trong các ngôn ngữ .Net, việc gộp kết nối được xử lý bởi các đối tượng ADO.Net theo mặc định (chuỗi kết nối đặt tất cả thông tin bắt buộc).

Cảm ơn Crad đã bình luận về điều này.


Tôi đoán việc sử dụng singleton mang lại cho tôi lợi thế là khởi tạo kết nối càng muộn càng tốt, trong khi nếu tôi sử dụng global, tôi có thể sẽ khởi tạo gần như ở đầu tập lệnh. Tôi có đúng không?
Imran

Đúng, nếu bạn đi theo con đường này, singleton sẽ mang lại lợi thế đó.
Dr8k

Trên thực tế, tất cả phụ thuộc vào quy mô. Trong triển khai web quy mô lớn, số lượng lớn các kết nối DB là xấu. Đây là lý do tại sao các ứng dụng như pgBouncer tồn tại cho PostgreSQL và Java thực hiện tổng hợp tài nguyên.
Gavin M. Roy

Đúng, nhưng tôi nghĩ rằng việc tổng hợp kết nối thích hợp khác với việc chỉ sử dụng các đối tượng hình nón toàn cục. Việc gộp kết nối vẫn sử dụng nhiều kết nối, nó chỉ giới hạn số lượng tối đa và sử dụng lại chúng theo thời gian để phân bổ chi phí thiết lập.
Dr8k

5
Lưu ý rằng "toàn cục" trong trường hợp PHP không làm cho biến toàn cầu trên các trang PHP. Nó chỉ có nghĩa là nó có thể được truy cập từ bên trong các chức năng.
Ates Goral

7

Phương thức singleton được tạo ra để đảm bảo rằng chỉ có một thể hiện của bất kỳ lớp nào. Tuy nhiên, vì mọi người sử dụng nó như một cách để tắt toàn cầu hóa, nó được gọi là lập trình lười biếng và / hoặc tồi tệ.

Do đó, tôi sẽ bỏ qua global và Singleton vì cả hai đều không thực sự là OOP.

Những gì bạn đang tìm kiếm là tiêm phụ thuộc .

Bạn có thể kiểm tra thông tin dựa trên PHP dễ đọc liên quan đến chèn phụ thuộc (với ví dụ) tại http://components.symfony-project.org/dependency-injection/trunk/book/01-Dependency-Injection


3

Cả hai mẫu đều đạt được hiệu ứng ròng như nhau, cung cấp một điểm truy cập duy nhất cho các lệnh gọi cơ sở dữ liệu của bạn.

Về cách triển khai cụ thể, singleton có một lợi thế nhỏ là không khởi tạo kết nối cơ sở dữ liệu cho đến khi có ít nhất một trong các phương thức khác của bạn yêu cầu. Trong thực tế trong hầu hết các ứng dụng tôi đã viết, điều này không tạo ra nhiều sự khác biệt, nhưng đó là một lợi thế tiềm năng nếu bạn có một số trang / đường dẫn thực thi không thực hiện bất kỳ lệnh gọi cơ sở dữ liệu nào, vì những trang đó sẽ không bao giờ yêu cầu kết nối với cơ sở dữ liệu.

Một khác biệt nhỏ khác là việc triển khai toàn cục có thể vô tình thay đổi các tên biến khác trong ứng dụng. Không có khả năng bạn sẽ vô tình khai báo một tham chiếu $ db toàn cục khác, mặc dù có thể bạn có thể vô tình ghi đè nó (giả sử bạn viết if ($ db = null) khi bạn định viết if ($ db == null). Đối tượng singleton ngăn cản điều đó.


2

Nếu bạn không sử dụng kết nối liên tục và có những trường hợp không làm được điều đó, thì tôi thấy một singleton hợp lý hơn về mặt khái niệm so với toàn cầu trong thiết kế OO.

Trong kiến ​​trúc OO thực sự, một singleton hiệu quả hơn việc tạo một thể hiện mới cho đối tượng mỗi lần.


2

Trong ví dụ đã cho, tôi thấy không có lý do gì để sử dụng các đĩa đơn. Theo nguyên tắc chung nếu mối quan tâm duy nhất của tôi là cho phép một phiên bản duy nhất của một đối tượng, nếu ngôn ngữ cho phép, tôi thích sử dụng hình cầu hơn


1

Nói chung, tôi sẽ sử dụng một singleton cho kết nối cơ sở dữ liệu ... Bạn không muốn tạo một kết nối mới mỗi khi bạn cần tương tác với cơ sở dữ liệu ... Điều này có thể ảnh hưởng đến hiệu suất và băng thông mạng của bạn ... Tại sao phải tạo một mới, khi có sẵn ... Chỉ 2 xu của tôi ...

RWendi


Với việc khởi tạo kết nối trong phạm vi toàn cục, tôi đang khởi tạo kết nối một lần trên mỗi trang và sử dụng biến toàn cục đó trong các hàm cần tương tác với cơ sở dữ liệu.
Imran

Sử dụng một singleton cho kết nối cơ sở dữ liệu không giống như không tạo lại kết nối sau mỗi lần tương tác với DBMS. Một singleton chỉ có vậy; một thể hiện của một lớp nhất định và là lớp duy nhất được phép tồn tại trên toàn cầu. Bạn có thể cần kết nối với các cơ sở dữ liệu khác nhau cùng một lúc.
Rob

tôi đang đề cập đến một lớp singleton quản lý các kết nối đến cơ sở dữ liệu. Tôi không thấy điểm của việc tạo một đối tượng kết nối mới mỗi khi bạn muốn tương tác với một dbms. Tất nhiên nếu bạn cần kết nối với cơ sở dữ liệu khác nhau cùng một lúc, bạn có thể cần tạo một đối tượng kết nối khác.
RWendi 25/09/08

0

Nó khá đơn giản. Không bao giờ sử dụng Global OR Singleton.


4
Khi nói đến mô hình Singleton, hãy luôn nói không bao giờ
1800 THÔNG TIN

3
Điều gì xảy ra nếu bạn muốn có nhiều hơn một nhà cung cấp nhật ký? Ai nói rằng tôi không thể đăng nhập vào một tệp và bảng điều khiển?
1800 INFORMATION

2
Vì vậy, bạn sau đó sẽ kết hợp một anti-pattern (Singleton) với nhau (Thiên Chúa Object)
1800 INFORMATION

3
Đúng. Và các nhà thiết kế của mọi ngôn ngữ OOP chính cũng vậy, bằng cách cho phép các đối tượng lớp toàn cục. Nếu bạn là người theo chủ nghĩa độc thần và bạn muốn một vật thể đại diện cho Chúa, thì thứ bạn đang tìm kiếm là một Vật thể Chúa Singleton. Bất cứ điều gì khác là không chính xác.
Steve Jessop 25/09/08

4
Nếu tôi là một người theo thuyết montheist và tôi muốn tạo một vật thể Chúa, tôi có thể chỉ định sử dụng một MonotheistAbstractFactory Pattern để tạo một vật thể Chúa của tôi? Điều này sẽ mở ra khả năng cho người dùng đa thần cũng sử dụng chương trình của tôi bằng cách chỉ định PolytheistAbstractfactory.
1800 INFORMATION

0

Theo lời khuyên, cả singletonglobal đều hợp lệ và có thể được tham gia trong cùng một hệ thống, dự án, plugin, sản phẩm, v.v. Trong trường hợp của tôi, tôi tạo các sản phẩm kỹ thuật số cho web (plugin).

Tôi chỉ sử dụng singleton trong lớp chính và tôi sử dụng nó theo nguyên tắc. Tôi gần như không sử dụng nó vì tôi biết rằng lớp chính sẽ không khởi tạo lại nó

<?php // file0.php

final class Main_Class
{
    private static $instance;
    private $time;

    private final function __construct()
    {
        $this->time = 0;
    }
    public final static function getInstance() : self
    {
        if (self::$instance instanceof self) {
            return self::$instance;
        }

        return self::$instance = new self();
    }
    public final function __clone()
    {
        throw new LogicException("Cloning timer is prohibited");
    }
    public final function __sleep()
    {
        throw new LogicException("Serializing timer is prohibited");
    }
    public final function __wakeup()
    {
        throw new LogicException("UnSerializing timer is prohibited");
    }
}

Sử dụng toàn cầu cho hầu hết các lớp phụ, ví dụ:

<?php // file1.php
global $YUZO;
$YUZO = new YUZO; // YUZO is name class

trong khi trong thời gian chạy, tôi có thể sử dụng Global để gọi các phương thức và thuộc tính của chúng trong cùng một trường hợp vì tôi không cần một phiên bản khác của lớp sản phẩm chính của mình.

<?php // file2.php
global $YUZO;
$YUZO->method1()->run();
$YUZO->method2( 'parameter' )->html()->print();

Tôi nhận được với toàn cầu là sử dụng cùng một phiên bản để có thể làm cho sản phẩm hoạt động vì tôi không cần nhà máy cho các phiên bản cùng lớp, thường thì nhà máy phiên bản dành cho các hệ thống lớn hoặc cho các mục đích rất hiếm.

In conclusion:, bạn phải nếu bạn đã hiểu rõ đó là Singleton chống mẫu và hiểu Toàn cục , bạn có thể sử dụng một trong 2 tùy chọn hoặc kết hợp chúng nhưng nếu tôi khuyên bạn không nên lạm dụng vì có nhiều lập trình viên rất ngoại lệ và trung thành với OOP lập trình, sử dụng nó cho các lớp chính và phụ mà bạn sử dụng nhiều trong thời gian thực thi. (Nó giúp bạn tiết kiệm rất nhiều CPU). 😉

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.