Có một trường hợp sử dụng cho các singletons có quyền truy cập cơ sở dữ liệu trong PHP không?


138

Tôi truy cập cơ sở dữ liệu MySQL của mình thông qua PDO. Tôi đang thiết lập quyền truy cập vào cơ sở dữ liệu và nỗ lực đầu tiên của tôi là sử dụng như sau:

Điều đầu tiên tôi nghĩ đến là global:

$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'root', 'pwd');

function some_function() {
    global $db;
    $db->query('...');
}

Đây được coi là một thực hành xấu. Sau một hồi tìm kiếm, tôi đã kết thúc với mẫu Singleton , mẫu này

"áp dụng cho các tình huống trong đó cần phải có một thể hiện duy nhất của một lớp."

Theo ví dụ trong hướng dẫn, chúng ta nên làm điều này:

class Database {
    private static $instance, $db;

    private function __construct(){}

    static function singleton() {
        if(!isset(self::$instance))
            self::$instance = new __CLASS__;

        return self:$instance;
    }

    function get() {
        if(!isset(self::$db))
            self::$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'user', 'pwd')

        return self::$db;
    }
}

function some_function() {
    $db = Database::singleton();
    $db->get()->query('...');
}

some_function();

Tại sao tôi cần lớp tương đối lớn khi tôi có thể làm điều này?

class Database {
    private static $db;

    private function __construct(){}

    static function get() {
        if(!isset(self::$db))
            self::$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'user', 'pwd');

        return self::$db;
    }
}

function some_function() {
    Database::get()->query('...');
}

some_function();

Cái cuối cùng này hoạt động hoàn hảo và tôi không cần phải lo lắng $dbnữa.

Làm cách nào tôi có thể tạo một lớp singleton nhỏ hơn hoặc có trường hợp sử dụng cho các singleton mà tôi bị thiếu trong PHP?


Có rất nhiều tài nguyên và thảo luận trong câu hỏi liên quan này: 'Điều gì là xấu về singletons?'
FruitBreak

Câu trả lời:


80

Được rồi, tôi đã tự hỏi về điều đó một thời gian khi tôi mới bắt đầu sự nghiệp của mình. Thực hiện theo nhiều cách khác nhau và đưa ra hai lý do để chọn không sử dụng các lớp tĩnh, nhưng chúng là những lý do khá lớn.

Một là bạn sẽ thấy rằng rất thường xuyên một cái gì đó mà bạn hoàn toàn chắc chắn rằng bạn sẽ không bao giờ có nhiều hơn một ví dụ, cuối cùng bạn có một thứ hai. Bạn có thể kết thúc với màn hình thứ hai, cơ sở dữ liệu thứ hai, máy chủ thứ hai - bất cứ điều gì.

Khi điều này xảy ra, nếu bạn đã sử dụng một lớp tĩnh, bạn sẽ sử dụng một bộ tái cấu trúc tồi tệ hơn nhiều so với khi bạn đã sử dụng một singleton. Bản thân một singleton là một mẫu iffy, nhưng nó chuyển đổi khá dễ dàng thành một mẫu nhà máy thông minh - thậm chí có thể được chuyển đổi sang sử dụng phép tiêm phụ thuộc mà không gặp quá nhiều khó khăn. Chẳng hạn, nếu singleton của bạn nhận được thông qua getInstance (), bạn có thể dễ dàng thay đổi điều đó thành getInstance (cơ sở dữ liệu) và cho phép nhiều cơ sở dữ liệu - không có thay đổi mã nào khác.

Vấn đề thứ hai là thử nghiệm (Và thành thật mà nói, đây là vấn đề đầu tiên). Đôi khi bạn muốn thay thế cơ sở dữ liệu của mình bằng cơ sở dữ liệu giả. Trong thực tế, đây là một ví dụ thứ hai của đối tượng cơ sở dữ liệu. Điều này khó thực hiện hơn với các lớp tĩnh so với các lớp đơn, bạn chỉ phải giả định phương thức getInstance (), không phải mọi phương thức đơn lẻ trong một lớp tĩnh (mà trong một số ngôn ngữ có thể rất khó).

Nó thực sự bắt nguồn từ thói quen - và khi mọi người nói "Quả cầu" là xấu, họ có lý do rất chính đáng để nói như vậy, nhưng điều đó có thể không phải lúc nào cũng rõ ràng cho đến khi bạn tự mình giải quyết vấn đề.

Điều tốt nhất bạn có thể làm là hỏi (giống như bạn đã làm) sau đó đưa ra lựa chọn và quan sát sự phân nhánh của quyết định của bạn. Có kiến ​​thức để diễn giải sự tiến hóa mã của bạn theo thời gian quan trọng hơn nhiều so với việc thực hiện nó ngay từ đầu.


15
Bạn nói rằng các singletons làm suy giảm độc đáo thành DI, nhưng không phải là ví dụ của bạn về việc getInstance(databaseName)chỉ phân tán các tham chiếu đến kho lưu trữ toàn bộ các thể hiện trong toàn bộ mã của bạn? Mã sẽ gọi getInstance(các) cá thể được nhập vào mã bởi mã máy khách và vì vậy không nên gọi getInstanceở vị trí đầu tiên.
Will Vousden

1
@Will Vousden Đúng, đó là một khoảng trống. Nó không thực sự là DI, nhưng nó có thể khá gần. Chẳng hạn, nếu đó là getInstance (hỗ trợ cơ sở dữ liệu) và trường hợp trả về được tính toán dựa trên cơ sở dữ liệu nào được truyền vào? Vấn đề là tránh làm mọi người sợ hãi với khung DI cho đến khi họ sẵn sàng cho nó.
Bill K

320

Singletons có rất ít - nếu không muốn nói là không - sử dụng trong PHP.

Trong các ngôn ngữ nơi các đối tượng sống trong bộ nhớ dùng chung, Singletons có thể được sử dụng để giữ mức sử dụng bộ nhớ thấp. Thay vì tạo hai đối tượng, bạn tham chiếu một thể hiện hiện có từ bộ nhớ ứng dụng được chia sẻ toàn cầu. Trong PHP không có bộ nhớ ứng dụng như vậy. Một Singleton được tạo trong một Yêu cầu sống cho chính xác yêu cầu đó. Một Singleton được tạo trong một Yêu cầu khác được thực hiện cùng một lúc vẫn là một trường hợp hoàn toàn khác. Do đó, một trong hai mục đích chính của Singleton không được áp dụng ở đây.

Ngoài ra, nhiều đối tượng chỉ có thể tồn tại về mặt khái niệm một lần trong ứng dụng của bạn không nhất thiết phải có cơ chế ngôn ngữ để thực thi điều này. Nếu bạn chỉ cần một ví dụ, thì đừng khởi tạo một ví dụ khác . Chỉ khi bạn không có trường hợp nào khác, ví dụ như khi mèo con chết khi bạn tạo phiên bản thứ hai, bạn có thể có Trường hợp sử dụng hợp lệ cho Singleton.

Mục đích khác là có một điểm truy cập toàn cầu đến một thể hiện trong cùng một Yêu cầu. Mặc dù điều này nghe có vẻ đáng mong đợi, nhưng nó thực sự không, bởi vì nó tạo ra sự khớp nối với phạm vi toàn cầu (giống như bất kỳ toàn cầu và thống kê nào). Điều này làm cho Kiểm thử đơn vị khó hơn và ứng dụng của bạn nói chung ít bảo trì hơn. Có nhiều cách để giảm thiểu điều này, nhưng nói chung, nếu bạn cần có cùng một ví dụ trong nhiều lớp, hãy sử dụng Dependency Injection .

Xem các trang trình bày của tôi cho Singletons trong PHP - Tại sao chúng xấu và cách bạn có thể loại bỏ chúng khỏi các ứng dụng của mình để biết thêm thông tin.

Ngay cả Erich Gamma , một trong những nhà phát minh của mẫu Singleton, cũng nghi ngờ mẫu này hiện nay:

"Tôi ủng hộ việc bỏ Singleton. Công dụng của nó hầu như luôn luôn là mùi thiết kế"

đọc thêm

Nếu, sau những điều trên, bạn vẫn cần trợ giúp quyết định:

Sơ đồ quyết định của Singleton


1
@Gordon vâng. Và ngay cả khi có thể duy trì các đối tượng giữa các yêu cầu, Singletons vẫn vi phạm một vài nguyên tắc RẮN và giới thiệu Nhà nước toàn cầu.
Gordon

4
Xin lỗi để đi ngược dòng chảy, nhưng DI không thực sự là một giải pháp cho vấn đề Singleton đang được sử dụng, trừ khi bạn hài lòng với các lớp có 42 tham số ctor (hoặc 42 lệnh gọi setFoo () và setBar () để thực hiện công việc). Vâng, một số ứng dụng, thật không may, phải được kết hợp này và phụ thuộc vào rất nhiều thứ bên ngoài. PHP là một ngôn ngữ keo, và đôi khi có rất nhiều thứ để gắn kết với nhau.
StasM

14
@StasM nếu bạn đang có 42 thông số ctor hoặc yêu cầu rất nhiều setters bạn đang làm sai. Vui lòng xem Clean Code Talks. Xin lỗi, nếu tôi không thể bận tâm để giải thích điều này một lần nữa. Hãy hỏi trong phòng chat PHP để biết thêm thông tin.
Gordon

@Gordon Phòng chat php ở đâu?
dùng658182

21

Ai cần singletons trong PHP?

Lưu ý rằng hầu hết tất cả sự phản đối đối với người độc thân đều xuất phát từ quan điểm kỹ thuật - nhưng họ cũng bị RẤT hạn chế trong phạm vi của họ. Đặc biệt là đối với PHP. Đầu tiên, tôi sẽ liệt kê một số lý do cho việc sử dụng singletons, và sau đó tôi sẽ phân tích những phản đối đối với việc sử dụng singletons. Đầu tiên, những người cần chúng:

- Những người đang mã hóa một khung / codebase lớn, sẽ được sử dụng trong nhiều môi trường khác nhau, sẽ phải làm việc với các khung / cơ sở mã khác nhau hiện có trước đây, với sự cần thiết phải thực hiện nhiều yêu cầu khác nhau, thậm chí thay đổi từ khách hàng / ông chủ / quản lý / lãnh đạo đơn vị làm.

Xem, mô hình singleton là bao gồm tự. Khi hoàn thành, một lớp singleton cứng nhắc trên bất kỳ mã nào bạn đưa nó vào và nó hoạt động chính xác như cách bạn tạo các phương thức và biến của nó. Và nó luôn luôn là cùng một đối tượng trong một yêu cầu nhất định. Vì nó không thể được tạo hai lần để trở thành hai đối tượng khác nhau, bạn biết đối tượng singleton là gì tại bất kỳ điểm đã cho nào trong mã - ngay cả khi singleton được chèn vào hai, ba cơ sở mã spaghetti khác nhau, thậm chí cũ. Do đó, nó giúp dễ dàng hơn về các mục đích phát triển - ngay cả khi có nhiều người làm việc trong dự án đó, khi bạn thấy một singleton được khởi tạo ở một điểm trong bất kỳ cơ sở mã nào, bạn sẽ biết nó là gì, nó làm gì, làm thế nào hiện và trạng thái của nó. Nếu đó là lớp truyền thống, bạn sẽ cần theo dõi xem đối tượng đó được tạo ra lần đầu tiên ở đâu, phương thức nào được gọi trong đó cho đến thời điểm đó trong mã và trạng thái cụ thể của nó. Tuy nhiên, hãy thả một singleton ở đó, và nếu bạn bỏ các phương thức gỡ lỗi và thông tin thích hợp và theo dõi vào singleton trong khi mã hóa nó, bạn sẽ biết chính xác nó là gì. Do đó, điều đó giúp những người phải làm việc với các cơ sở mã khác nhau dễ dàng hơn, với sự cần thiết phải tích hợp mã được thực hiện trước đó với các triết lý khác nhau hoặc được thực hiện bởi những người bạn không liên hệ. (nghĩa là, nhà cung cấp-dự án-công ty-bất cứ điều gì không còn nữa, không hỗ trợ gì cả). nó giúp những người phải làm việc với các cơ sở mã khác nhau dễ dàng hơn, với sự cần thiết phải tích hợp mã được thực hiện trước đó với các triết lý khác nhau hoặc được thực hiện bởi những người bạn không liên hệ. (nghĩa là, nhà cung cấp-dự án-công ty-bất cứ điều gì không còn nữa, không hỗ trợ gì cả). nó giúp những người phải làm việc với các cơ sở mã khác nhau dễ dàng hơn, với sự cần thiết phải tích hợp mã được thực hiện trước đó với các triết lý khác nhau hoặc được thực hiện bởi những người bạn không liên hệ. (nghĩa là, nhà cung cấp-dự án-công ty-bất cứ điều gì không còn nữa, không hỗ trợ gì cả).

- Những người cần làm việc với bên thứ ba API , dịch vụ và trang web .

Nếu bạn nhìn kỹ hơn, điều này không quá khác biệt so với trường hợp trước đó - API, dịch vụ, trang web của bên thứ ba, giống như các cơ sở mã hóa bên ngoài, bị cô lập mà bạn KHÔNG có quyền kiểm soát. Chuyện gì cũng có thể xảy ra. Vì vậy, với một phiên người dùng / lớp người dùng, bạn có thể quản lý BẤT K type loại thực hiện phiên / ủy quyền nào từ các nhà cung cấp bên thứ ba như OpenID , Facebook , Twitter và nhiều hơn nữa - và bạn có thể thực hiện TẤT CẢ những điều này cùng một lúc từ đối tượng đơn SAME - có thể truy cập dễ dàng, ở trạng thái đã biết tại bất kỳ điểm nào trong bất kỳ mã nào bạn cắm nó vào. Bạn thậm chí có thể tạo nhiều phiên cho nhiều API / dịch vụ bên thứ ba khác nhau cho người dùng CÙNG trong trang web / ứng dụng của riêng bạn và làm bất cứ điều gì bạn muốn làm với họ.

Tất nhiên, tất cả những điều này cũng có thể đồng điệu với các phương thức truyền thống bằng cách sử dụng các lớp và đối tượng bình thường - điều đáng chú ý ở đây là, singleton gọn gàng hơn, gọn gàng hơn và do đó có thể quản lý / kiểm tra dễ dàng hơn so với việc sử dụng lớp / đối tượng truyền thống trong các tình huống như vậy.

- Những người cần phát triển nhanh

Hành vi giống như toàn cầu của singletons giúp dễ dàng xây dựng bất kỳ loại mã nào với khung có tập hợp các singletons để xây dựng, bởi vì một khi bạn xây dựng tốt các lớp singleton của mình, các phương thức được thiết lập, trưởng thành và thiết lập sẽ dễ dàng có sẵn và có thể sử dụng mọi lúc, mọi nơi, một cách nhất quán. Phải mất một thời gian để trưởng thành các lớp học của bạn, nhưng sau đó, chúng là đá vững chắc và nhất quán, và hữu ích. Bạn có thể có nhiều phương thức trong một singleton làm bất cứ điều gì bạn muốn, và mặc dù điều này có thể làm tăng dung lượng bộ nhớ của đối tượng, nó mang lại sự tiết kiệm nhiều thời gian hơn cần thiết cho sự phát triển nhanh chóng - một phương pháp bạn không sử dụng trong một ví dụ cụ thể một ứng dụng có thể được sử dụng trong một ứng dụng tích hợp khác và bạn có thể sử dụng một tính năng mới mà khách hàng / sếp / người quản lý dự án yêu cầu chỉ bằng một vài sửa đổi.

Bạn có được ý tưởng. Bây giờ chúng ta hãy chuyển sang sự phản đối đối với những người độc thân và cuộc thập tự chinh không lành mạnh chống lại điều gì đó hữu ích :

- Phản đối quan trọng nhất là nó làm cho việc kiểm tra khó khăn hơn.

Và thực sự, nó ở một mức độ nào đó, ngay cả khi nó có thể được giảm nhẹ một cách dễ dàng bằng cách thực hiện các biện pháp phòng ngừa và mã hóa đúng cách vào các singletons của bạn VỚI việc nhận ra rằng bạn sẽ gỡ lỗi một singleton. Nhưng hãy xem, điều này không quá khác biệt so với BẤT K phil triết lý / phương pháp / mô hình mã hóa nào khác ngoài đó - chỉ là, các singletons còn khá mới và không phổ biến, vì vậy các phương pháp thử nghiệm hiện tại cuối cùng không tương thích với chúng. Nhưng điều đó không khác biệt trong bất kỳ khía cạnh nào của ngôn ngữ lập trình - các phong cách khác nhau đòi hỏi các cách tiếp cận khác nhau.

Một điểm của sự phản đối này nằm ở chỗ, nó bỏ qua thực tế là lý do các ứng dụng được phát triển không phải để 'thử nghiệm' và thử nghiệm không phải là giai đoạn / quy trình duy nhất đi vào phát triển ứng dụng. Các ứng dụng được phát triển để sử dụng sản xuất. Và như tôi đã giải thích trong phần 'ai cần người độc thân', người độc thân có thể cắt giảm một thỏa thuận TUYỆT VỜI khỏi sự phức tạp của việc phải làm cho một mã hoạt động VỚI và NỘI BỘ nhiều dịch vụ / ứng dụng / dịch vụ của bên thứ ba khác nhau. Thời gian có thể bị mất trong thử nghiệm, là thời gian đạt được trong quá trình phát triển và triển khai. Điều này đặc biệt hữu ích trong thời đại xác thực / ứng dụng / tích hợp của bên thứ ba - Facebook, Twitter, OpenID, nhiều hơn nữa và những người biết những gì tiếp theo.

Mặc dù đó là điều dễ hiểu - các lập trình viên làm việc trong những hoàn cảnh rất khác nhau tùy thuộc vào nghề nghiệp của họ. Và đối với những người làm việc trong các công ty tương đối lớn với các bộ phận được xác định có xu hướng khác nhau, phần mềm / ứng dụng được xác định một cách thoải mái và không có sự cắt giảm ngân sách sắp xảy ra và cần phải thực hiện RẤT NHIỀU công cụ với nhiều thứ khác nhau một thời trang rẻ / nhanh / đáng tin cậy, singletons có vẻ không cần thiết lắm. Và nó thậm chí có thể gây phiền toái / cản trở cho những gì họ CRENG CÓ.

Nhưng đối với những người cần làm việc trong các rãnh bẩn của sự phát triển 'nhanh nhẹn', phải thực hiện nhiều yêu cầu khác nhau (đôi khi không hợp lý) từ khách hàng / người quản lý / dự án của họ, singletons là một ân huệ tiết kiệm vì lý do đã giải thích trước đó.

- Một phản đối khác là dấu chân bộ nhớ của nó cao hơn

Bởi vì một singleton mới sẽ tồn tại cho mỗi yêu cầu từ mỗi khách hàng, điều này có thể là một sự phản đối đối với PHP. Với các singletons được xây dựng và sử dụng kém, dung lượng bộ nhớ của ứng dụng có thể cao hơn nếu nhiều người dùng được ứng dụng phục vụ tại bất kỳ điểm nào.

Mặc dù, điều này là hợp lệ cho BẤT CỨ cách tiếp cận nào bạn có thể thực hiện trong khi mã hóa mọi thứ. Các câu hỏi nên được đặt ra là, các phương pháp, dữ liệu được giữ và xử lý bởi các singletons này có cần thiết không? Đối với, nếu chúng là cần thiết trên nhiều ứng dụng yêu cầu, thì ngay cả khi bạn không sử dụng singletons, các phương thức và dữ liệu đó SILL hiện diện trong ứng dụng của bạn dưới dạng này hay dạng khác thông qua mã. Vì vậy, tất cả trở thành một câu hỏi về việc bạn sẽ tiết kiệm được bao nhiêu bộ nhớ, khi bạn khởi tạo một đối tượng lớp truyền thống 1/3 vào quá trình xử lý mã và phá hủy 3/4 nó vào nó.

Hãy xem, khi đặt theo cách này, câu hỏi trở nên khá không liên quan - không nên có các phương pháp không cần thiết, dữ liệu được giữ trong các đối tượng trong mã của bạn BẤT K - - bất kể bạn có sử dụng singletons hay không. Vì vậy, sự phản đối này đối với các singletons trở nên thực sự vui nhộn ở chỗ, nó ĐÁNH GIÁ rằng sẽ có các phương thức, dữ liệu không cần thiết trong các đối tượng được tạo từ các lớp bạn sử dụng.

- Một số phản đối không hợp lệ như 'làm cho việc duy trì nhiều kết nối cơ sở dữ liệu là không thể / khó hơn'

Tôi thậm chí không thể bắt đầu hiểu được sự phản đối này, khi tất cả một người cần duy trì nhiều kết nối cơ sở dữ liệu, nhiều lựa chọn cơ sở dữ liệu, nhiều truy vấn cơ sở dữ liệu, nhiều tập kết quả trong một singleton chỉ là giữ chúng trong các biến / mảng trong singleton miễn là chúng là cần thiết Điều này có thể đơn giản như giữ chúng trong mảng, mặc dù bạn có thể phát minh ra bất kỳ phương pháp nào bạn muốn sử dụng để thực hiện điều đó. Nhưng hãy xem xét trường hợp đơn giản nhất, sử dụng các biến và mảng trong một đơn lẻ nhất định:

Hãy tưởng tượng bên dưới là bên trong một cơ sở dữ liệu nhất định:

$ this -> links = mảng (); (cú pháp sai, tôi chỉ gõ nó như thế này để cung cấp cho bạn hình ảnh - khai báo đúng của biến là public $ links = Array (); và cách sử dụng của nó là $ this-> links ['Connectionkey'] một cách tự nhiên)

Bạn có thể thiết lập và giữ nhiều kết nối tại bất kỳ thời điểm nào trong một mảng theo kiểu này. Và tương tự với các truy vấn, tập kết quả và vv.

$ này -> truy vấn (QUERYSTRING, 'tên truy vấn', $ this-> kết nối ['particulrconnection']);

Mà chỉ có thể thực hiện một truy vấn đến cơ sở dữ liệu đã chọn với một kết nối được chọn và chỉ cần lưu trữ trong

$ này -> kết quả

mảng với khóa 'queryname'. Tất nhiên, bạn sẽ cần phải mã hóa phương thức truy vấn của mình cho việc này - điều này không quan trọng để làm.

Điều này cho phép bạn duy trì số lượng gần như vô hạn (bao nhiêu giới hạn tài nguyên cho phép tất nhiên) các kết nối cơ sở dữ liệu khác nhau và các tập kết quả nhiều như bạn cần chúng. Và chúng có sẵn cho MỌI đoạn mã ở bất kỳ điểm đã cho nào trong bất kỳ cơ sở mã đã cho nào mà lớp đơn này đã được khởi tạo.

KHÓA HỌC, đương nhiên bạn sẽ cần giải phóng các tập kết quả và các kết nối khi không cần thiết - nhưng điều đó không cần phải nói, và nó không đặc trưng cho singletons hoặc bất kỳ phương pháp / phong cách / khái niệm mã hóa nào khác.

Tại thời điểm này, bạn có thể thấy cách bạn có thể duy trì nhiều kết nối / trạng thái cho các ứng dụng hoặc dịch vụ của bên thứ ba trong cùng một đơn. Không quá khác biệt.

Câu chuyện dài, cuối cùng, các mẫu đơn lẻ chỉ là một phương pháp / phong cách / triết lý khác để lập trình và chúng cũng hữu ích như BẤT K other khi chúng được sử dụng đúng nơi, đúng cách. Mà không khác gì.

Bạn sẽ nhận thấy rằng trong hầu hết các bài viết trong đó các singletons bị bash, bạn cũng sẽ thấy các tài liệu tham khảo về 'toàn cầu' là 'ác quỷ'.

Hãy đối mặt với nó - Bất cứ điều gì không được sử dụng đúng cách, lạm dụng, lạm dụng, là xấu xa. Điều đó không giới hạn ở bất kỳ ngôn ngữ, bất kỳ khái niệm mã hóa, phương pháp nào. Bất cứ khi nào bạn thấy ai đó đưa ra những tuyên bố về chăn như 'X là xấu xa', hãy chạy khỏi bài báo đó. Rất có thể đó là sản phẩm của một quan điểm hạn chế - ngay cả khi quan điểm đó là kết quả của nhiều năm kinh nghiệm trong một điều gì đó - mà nói chung cuối cùng là kết quả của việc làm việc quá nhiều trong một phong cách / phương pháp nhất định - chủ nghĩa bảo thủ trí tuệ điển hình.

Ví dụ vô tận có thể được đưa ra cho điều đó, từ 'toàn cầu là xấu xa' đến 'iframes là ác'. Quay trở lại khoảng 10 năm trước, thậm chí đề xuất sử dụng iframe trong bất kỳ ứng dụng cụ thể nào là dị giáo. Sau đó, đến Facebook, iframe ở khắp mọi nơi và xem xét những gì đã xảy ra - iframe không còn quá xấu xa nữa.

Vẫn có những người ngoan cố khăng khăng rằng họ là 'ác quỷ' - và đôi khi cũng vì lý do chính đáng - nhưng, như bạn có thể thấy, có một nhu cầu, iframes đáp ứng nhu cầu đó và hoạt động tốt, và do đó toàn bộ thế giới chỉ tiếp tục.

Tài sản quan trọng nhất của một lập trình viên / lập trình viên / kỹ sư phần mềm là một tâm trí tự do, cởi mở và linh hoạt.


2
-1. Mặc dù tôi đồng ý rằng có một tâm trí cởi mở và linh hoạt là tài sản bắt buộc đối với bất kỳ nhà phát triển nào, nhưng điều đó không giúp Singleton trở thành một Antipotype. Câu trả lời ở trên chứa rất nhiều tuyên bố không chính xác và kết luận sai về bản chất và tác dụng của Singleton mà tôi không thể làm được.
Gordon

-1. Tôi đã phải trải nghiệm một khuôn khổ với nhiều singletons và việc kiểm tra tự động là không thể. Tôi phải tự kiểm tra mọi thứ thông qua bản dùng thử và lỗi trong trình duyệt. Một số lỗi có thể được ngăn chặn bằng cách xem lại mã (lỗi chính tả, lỗi cú pháp) nhưng lỗi chức năng thường bị ẩn. Thử nghiệm này đòi hỏi nhiều thời gian hơn sau đó thử nghiệm đơn vị sẽ. Với các bài kiểm tra đơn vị tôi có thể nói: Lớp này hoạt động độc lập, lỗi phải ở một nơi khác. Không có gỡ lỗi là tẻ nhạt.
Jim Martens

Khung phải được xây dựng trong đăng nhập và theo dõi lỗi. Ngoài ra, một lớp hoạt động đúng cách trong sự cô lập, cũng sẽ hoạt động đúng ở dạng đơn lẻ khi được đưa vào một ứng dụng rộng hơn. Điều đó có nghĩa là trong trường hợp đó, thứ bị phá vỡ sẽ là một lớp hoặc hàm khác đang tương tác với singleton đó. Điều này không khác gì theo dõi lỗi thông thường trong một ứng dụng lớn. Mà chính nó là khá khó khăn mà không có ứng dụng có đăng nhập thích hợp.
unity100

Không chính xác. Hàng tấn singletones chắc chắn là EVIL, bởi vì nó tạo ra Thử nghiệm-HELL. :-) Tuy nhiên, một singletone cho mỗi ứng dụng có thể tốt. Ví dụ: như một tính năng ghi nhật ký hợp nhất - để triển khai trên tất cả các ứng dụng (bao gồm cả một số mã kế thừa).
Filip OvertoneSinger Rydlo

"Thời gian có thể bị mất trong thử nghiệm ..." Đây là một thực tế và cách suy nghĩ thực sự tồi tệ. Tất cả những ứng dụng cũ được phát triển với ý tưởng này và không thể duy trì chúng vì vậy chúng cần phải được viết lại. Nếu không có thử nghiệm, thời gian sẽ bị mất khi một tính năng mới được phát triển và phá vỡ một cái gì đó trong một số phần khác của hệ thống. Mất thời gian khi gỡ lỗi, mất thời gian bởi người dùng có thể sử dụng tính năng đó đúng cách, mất niềm tin vào ứng dụng, v.v.
bogdancep

15

Singletons được nhiều người coi là chống mẫu vì họ thực sự chỉ là những biến số toàn cầu được tôn vinh. Trên thực tế có rất ít tình huống mà nó cần thiết cho một lớp chỉ có một ví dụ; thông thường chỉ cần một ví dụ là đủ , trong trường hợp đó, việc thực hiện nó như một singleton là hoàn toàn không cần thiết.

Để trả lời câu hỏi, bạn đã đúng rằng những người độc thân quá mức cần thiết ở đây. Một biến hoặc hàm đơn giản sẽ làm. Tuy nhiên, một cách tiếp cận tốt hơn (mạnh mẽ hơn) sẽ là sử dụng phương pháp tiêm phụ thuộc để loại bỏ hoàn toàn nhu cầu về các biến toàn cục.


Nhưng Singletons có thể phân hủy rất trơn tru thành DI, các lớp tĩnh không thể, đó là vấn đề thực sự với các lớp tĩnh.
Bill K

@Bill: Rất đúng, nhưng đó là lý do tại sao tôi ủng hộ cách tiếp cận DI bắt đầu, thay vì các hàm lỏng lẻo hoặc phương thức tĩnh :)
Will Vousden

Trong một số ngôn ngữ (như Java) các lớp tĩnh (hoặc các phương thức tĩnh của các lớp) không thể được mở rộng. Vì vậy, bạn tạo ra các vấn đề tiềm năng (hoặc tốt nhất, nhiều công việc hơn) cho các nhà phát triển trong tương lai. Vì vậy, một số gợi ý rằng các phương thức tĩnh thường nên tránh trừ khi bạn có nhu cầu cụ thể đối với chúng.
Marvo

8

Trong ví dụ của bạn, bạn đang xử lý một mẩu thông tin dường như không thay đổi. Trong ví dụ này, Singleton sẽ quá mức cần thiết và chỉ cần sử dụng hàm tĩnh trong một lớp sẽ hoạt động tốt.

Thêm suy nghĩ: Bạn có thể gặp phải trường hợp triển khai các mẫu vì lợi ích của các mẫu và ruột của bạn đang nói với bạn "không, bạn không phải" vì những lý do bạn đánh vần.

NHƯNG: Chúng tôi không có ý tưởng về quy mô và phạm vi dự án của bạn. Nếu đây là mã đơn giản, có thể vứt đi, điều đó không có khả năng cần thay đổi thì có, hãy tiếp tục và sử dụng các thành viên tĩnh. Nhưng, nếu bạn nghĩ rằng dự án của bạn có thể cần phải mở rộng quy mô hoặc được chuẩn bị trước để bảo trì mã hóa trên đường thì, vâng, bạn có thể muốn sử dụng mẫu Singleton.


1
Wow, chỉ đơn giản là sai. Điểm chung của sự khác biệt (câu trả lời cho câu hỏi) là việc sửa mã của bạn để thêm một ví dụ thứ hai khó hơn bao nhiêu. Sẽ khó hơn nhiều nếu bạn sử dụng các phương thức tĩnh. Điều này giống như nói "Globals vẫn ổn trong điều kiện hạn chế của bạn" khi toàn bộ vấn đề với Globals là điều kiện thay đổi.
Bill K

@Bill K: Tôi đồng ý với bạn và tôi sẽ sử dụng một singleton nếu có bất kỳ sự phức tạp nào. Nhưng tôi đã cố gắng trả lời câu hỏi theo quan điểm và suy nghĩ của OP, ừ, tôi đoán là nó quá mức cần thiết trong trường hợp rất hạn chế này. Tất nhiên tôi đã bỏ qua các mối quan tâm về kiến ​​trúc hoặc khả năng mở rộng và một loạt các cân nhắc khác. Tôi có nên đưa vào đó như một lời cảnh báo trong câu trả lời của mình liên tục với một lời giải thích về lý do tại sao một người nào đó nên luôn luôn sử dụng một đơn ... điều này chắc chắn sẽ gây ra sự phản đối từ người khác?
Paul Sasik

5

Đầu tiên, tôi chỉ muốn nói rằng tôi không tìm thấy nhiều cách sử dụng cho mẫu Singleton. Tại sao người ta muốn giữ một đối tượng duy nhất xuyên suốt toàn bộ ứng dụng? Đặc biệt đối với cơ sở dữ liệu, nếu tôi muốn kết nối với máy chủ cơ sở dữ liệu khác thì sao? Tôi phải ngắt kết nối và kết nối lại mỗi lần ...? Dù sao...

Có một số nhược điểm khi sử dụng toàn cầu trong một ứng dụng (đó là cách sử dụng truyền thống của mẫu Singleton):

  • Khó kiểm tra đơn vị
  • Vấn đề tiêm phụ thuộc
  • Có thể tạo sự cố khóa (ứng dụng đa luồng)

Sử dụng các lớp tĩnh thay vì một cá thể singleton cũng cung cấp một số nhược điểm tương tự, bởi vì vấn đề lớn nhất của singleton là tĩnh getInstance phương thức .

Bạn có thể giới hạn số lượng phiên bản mà một lớp có thể có mà không cần sử dụng getInstancephương thức truyền thống :

class Single {

    static private $_instance = false;

    public function __construct() {
        if (self::$_instance)
           throw new RuntimeException('An instance of '.__CLASS__.' already exists');

        self::$_instance = true;
    }

    private function __clone() {
        throw new RuntimeException('Cannot clone a singleton class');
    }

    public function __destruct() {
        self::$_instance = false;
    }

}

$a = new Single;
$b = new Single; // error
$b = clone($a); // error
unset($a);
$b = new Single; // works

Điều này sẽ giúp về điểm đầu tiên được đề cập ở trên: thử nghiệm đơn vị và tiêm phụ thuộc; trong khi vẫn đảm bảo một thể hiện duy nhất của lớp tồn tại trong ứng dụng của bạn. Ví dụ, bạn có thể chuyển đối tượng kết quả cho các mô hình của mình (mẫu MVC) để chúng sử dụng.


5

Hãy xem xét đơn giản cách giải pháp của bạn khác với giải pháp được trình bày trong các tài liệu PHP. Trong thực tế, chỉ có một sự khác biệt "nhỏ": giải pháp của bạn cung cấp cho người gọi của getter một PDOví dụ, trong khi một trong các tài liệu cung cấp cho người gọi Database::singletonvới một Databasethể hiện (sau đó họ sử dụng getter trên đó để lấy một PDOthể hiện).

Vậy kết luận nào chúng ta đạt được?

  • Trong mã tài liệu, người gọi có được một Databaseví dụ. Các Databaselớp học có thể tiếp xúc với (trên thực tế, nó nên phơi bày nếu bạn 'tái đi đến tất cả các rắc rối này) một giao diện phong phú hơn hoặc cấp cao hơn so với PDOđối tượng mà nó kết thúc tốt đẹp.
  • Nếu bạn thay đổi triển khai của mình để trả về loại khác (phong phú hơn) PDO, thì hai triển khai là tương đương. Không có lợi ích gì để có được sau khi thực hiện thủ công.

Về mặt thực tế, Singleton là một mô hình khá gây tranh cãi. Điều này chủ yếu là vì:

  • Nó được sử dụng quá mức. Các lập trình viên Novice mò mẫm Singleton dễ dàng hơn nhiều so với các mẫu khác. Sau đó, họ tiếp tục áp dụng kiến ​​thức mới phát hiện của mình ở khắp mọi nơi, ngay cả khi vấn đề trong tay có thể được giải quyết tốt hơn mà không cần Singleton (khi bạn đang cầm búa, mọi thứ trông giống như một cái đinh).
  • Tùy thuộc vào ngôn ngữ lập trình, việc triển khai Singleton một cách kín đáo, không rò rỉ có thể chứng minh là một nhiệm vụ lớn (đặc biệt nếu chúng ta có các kịch bản nâng cao: một singleton tùy thuộc vào một singleton khác, các singleton có thể bị phá hủy và tạo lại, v.v. ). Chỉ cần cố gắng tìm kiếm triển khai Singleton "dứt khoát" trong C ++, tôi dám bạn (Tôi sở hữu Thiết kế C ++ hiện đại đột phá của Andrei Alexandrescu, tài liệu về sự lộn xộn).
  • Nó áp đặt khối lượng công việc bổ sung cả khi mã hóa Singleton và khi viết mã để truy cập vào nó, khối lượng công việc bạn có thể làm mà không cần tuân theo một vài ràng buộc tự áp đặt đối với những gì bạn cố gắng thực hiện với các biến chương trình của mình.

Vì vậy, như là một kết luận cuối cùng: singleton của bạn là tốt. Không sử dụng Singleton chút nào cũng tốt trong hầu hết thời gian.


2

Giải thích của bạn là chính xác. Singletons có vị trí của họ nhưng bị lạm dụng. Thông thường, truy cập các chức năng thành viên tĩnh là đủ (đáng chú ý, khi bạn không cần kiểm soát thời gian xây dựng theo bất kỳ cách nào). Tốt hơn, bạn chỉ có thể đặt một số hàm và biến miễn phí trong một không gian tên.


2

Khi lập trình không có "đúng" và "sai"; có "thực hành tốt" và "thực hành xấu".

Singletons thường được tạo ra như là một lớp sẽ được sử dụng lại sau này. Chúng cần được tạo theo cách mà lập trình viên không vô tình khởi tạo hai trường hợp trong khi say sưa mã hóa vào nửa đêm.

Nếu bạn có một lớp nhỏ đơn giản không nên khởi tạo nhiều lần, bạn không cần phải biến nó thành một lớp đơn. Đó chỉ là một mạng lưới an toàn nếu bạn làm.

Nó không phải luôn luôn là thực hành xấu để có các đối tượng toàn cầu. Nếu bạn biết rằng bạn sẽ sử dụng nó trên toàn cầu / mọi nơi / mọi lúc, thì đó có thể là một trong số ít trường hợp ngoại lệ. Tuy nhiên, toàn cầu thường được coi là "thực hành xấu" theo cùng một cách gotođược coi là thực tiễn xấu.


2

Tôi không thấy bất kỳ điểm nào về điều này cả. Nếu bạn đã triển khai lớp theo cách mà chuỗi kết nối được lấy làm tham số cho hàm tạo và duy trì một danh sách các đối tượng PDO (một cho mỗi chuỗi kết nối duy nhất) thì có thể sẽ có một số lợi ích, nhưng việc triển khai singleton trong trường hợp này có vẻ như một bài tập vô nghĩa.


1

Bạn không thiếu thứ gì, theo như tôi có thể thấy. Ví dụ là khá thiếu sót. Nó sẽ làm cho sự khác biệt, nếu lớp singleton có một số biến đối tượng không tĩnh.

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.