Lựa chọn thay thế cho Singletons / toàn cầu


16

Tôi đã nghe vô số lần về những cạm bẫy của Singletons / toàn cầu và tôi hiểu tại sao họ thường cau mày như vậy.

Điều tôi không hiểu là sự thay thế thanh lịch, không lộn xộn là gì. Có vẻ như giải pháp thay thế cho việc sử dụng Singletons / toàn cầu luôn liên quan đến việc chuyển các đối tượng xuống một triệu cấp thông qua các đối tượng động cơ của bạn cho đến khi chúng tiếp cận các đối tượng cần chúng.

Ví dụ: trong trò chơi của tôi, tôi tải trước một số tài sản khi trò chơi bắt đầu. Những tài sản này không được sử dụng cho đến sau này khi người chơi điều hướng qua menu chính và vào trò chơi. Tôi có nên chuyển dữ liệu này từ đối tượng Trò chơi của mình sang đối tượng ScreenManager của mình không (mặc dù thực tế là chỉ có một Màn hình thực sự quan tâm đến dữ liệu này), sau đó đến đối tượng Màn hình thích hợp và bất kỳ nơi nào khác?

Có vẻ như tôi đang giao dịch dữ liệu trạng thái toàn cầu để tiêm phụ thuộc lộn xộn, truyền dữ liệu đến các đối tượng thậm chí không quan tâm đến dữ liệu ngoại trừ mục đích truyền dữ liệu đó cho các đối tượng con.

Đây có phải là một trường hợp mà một Singleton sẽ là một điều tốt, hoặc có một số giải pháp thanh lịch mà tôi đang thiếu?

Câu trả lời:


16

Đừng giới thiệu singletons và toàn cầu. Mặc dù một số loại biến toàn cục thường là cần thiết, nhưng singleton không chỉ là sự thay thế cho biến toàn cục, mà chủ yếu là cách khắc phục các vấn đề về thứ tự khởi tạo tĩnh trong C ++ ( và FQA ). (Trong các ngôn ngữ khác, đó là một cách để khắc phục các thiếu sót ngôn ngữ khác nhau, như thiếu các biến toàn cục và các hàm trần.)

Nếu bạn chỉ có thể sử dụng một con trỏ chung thay vì một đơn và đảm bảo rằng nó đã được khởi tạo (thủ công) trước khi có bất cứ thứ gì cần, bạn sẽ tránh được lệnh gọi hàm và chi phí, cú pháp khập khiễng ở đối tượng và bạn thực sự có thể thực hiện thể hiện thứ hai của lớp khi bạn cần kiểm tra hoặc do thiết kế của bạn thay đổi.

Đối với một vài biến toàn cục mà bạn muốn (ví dụ phổ biến là đầu ra âm thanh, danh sách các cửa sổ đang mở, trình xử lý bàn phím, v.v.), tôi khuyên bạn nên sử dụng mẫu định vị dịch vụ . Nó giúp dễ dàng thay thế mọi thứ bằng các triển khai khác nhau (ví dụ: thiết bị âm thanh thực so với null) và thu thập tất cả các phần chung của bạn vào một cấu trúc để tránh gây ô nhiễm không gian tên của bạn.


+1. Câu trả lời tốt và cảm ơn cho các liên kết mô hình định vị dịch vụ. Đó là một bài đọc rất thú vị.
bummzack

1

Nếu bạn không / không thể có một phần của mã "biết" một cách kỳ diệu về một số dữ liệu, thì nó sẽ cần phải được thông qua bằng cách nào đó. Tuy nhiên, điều đó không có nghĩa là nó nhất thiết phải được thông qua chỉ thông qua các đối số.

Trong trường hợp ví dụ của bạn, bạn có thể không có một loại "AssetManager" nào có thể tải và lưu trữ tài sản không, và sau đó Trình quản lý màn hình sẽ chỉ cần được cung cấp một tham chiếu đến điều đó (có thể là lúc tạo)? Theo nghĩa đó, bạn chuyển các tham chiếu đến các tài sản được gói trong một đối tượng khác và bạn có thể chuyển nó một lần, khi khởi tạo, thay vì chuyển nó xuống hàm lá khi cần.

Bây giờ IMHO rằng AssetManager, là loại điều bạn chỉ muốn một trong số đó, cũng có thể là một người độc thân. Miễn là bạn hiểu những cạm bẫy và mã cụ thể để tránh chúng (giả sử rằng singleton sẽ được truy cập đồng thời từ nhiều luồng và tự đâm bằng nĩa bất cứ khi nào bạn làm điều gì đó cần chặn), sau đó tự mình thoát ra.


1

Tôi nghĩ Jason D hoàn toàn đúng - đây là cách tôi sẽ xử lý nó:

Trò chơi có một phiên bản của AssetManager, một đối tượng mà bạn có thể nhận bất kỳ tài sản nào theo tên.

Trong game:

assetManager = new AssetManager();
screenManager = new ScreenManager();
screenManager.assetManager = assetManager;

Trong Trình quản lý màn hình:

screen = new Screen();
screen.assetManager = assetManager;

Trong màn hình:

myAsset = assetManager.getBitmp("lava.png");

Bây giờ tất cả các màn hình có quyền truy cập vào bất kỳ tài sản họ cần. Điều này không phức tạp hay điên rồ hơn việc sử dụng toàn cầu hoặc Singletons và bạn có tùy chọn để có 2 phiên bản Trò chơi chạy trong cùng một ứng dụng mà không bị đụng độ. Tôi đã từng phải làm một trò chơi được tạo thành từ 8 trò chơi nhỏ, tất cả đều có chung các lớp / khung cơ sở. Tôi đã phải cấu trúc lại tất cả các thế giới / singletons của mình để sử dụng kiểu chuyển tham chiếu này và tôi không bao giờ nhìn lại. Những thứ duy nhất nên có trên toàn cầu là những thứ chỉ có thể tồn tại một lần về mặt vật lý, chẳng hạn như âm thanh, mạng, i / o, v.v.


0

Bạn có thể sử dụng mẫu Factory để thay thế Singleton . Sau đó, lớp nhà máy có quyền kiểm soát số lượng phiên bản bạn có thể tạo, mà bạn có thể dễ dàng thay đổi sau này khi bạn thấy bạn cần nhiều hơn một AssetManager. Như đã nêu trong bài viết này :

Nó cung cấp cho bạn tất cả sự linh hoạt của Singleton, không có nhiều vấn đề.


Một khả năng khác, khá hạn chế, là làm cho lớp tĩnh (mà tôi không nghĩ là khả thi đối với AssetManager và chỉ có thể có trong các ngôn ngữ có các lớp tĩnh). Nhưng điều đó chỉ hoạt động nếu bạn không cần kế thừa / đa hình. Đó là một giải pháp rất không linh hoạt:

phương pháp tĩnh linh hoạt như đá granit. Mỗi khi bạn sử dụng một, bạn sẽ thực hiện một phần chương trình của mình một cách cụ thể. Chỉ cần chắc chắn rằng bạn không bị kẹt chân ở đó khi bạn đang xem nó cứng lại. Một ngày nào đó bạn sẽ ngạc nhiên rằng, bởi trời ạ, bạn thực sự cần một triển khai khác của lớp PrintSpooler đó, và nó phải là một giao diện, một nhà máy và một tập hợp các lớp thực hiện. Ôi!

Đây là về các phương thức tĩnh, nhưng nó cũng có thể được áp dụng cho các lớp tĩnh.


Bạn có ý nghĩa gì khi "làm cho lớp tĩnh"? C ++ không có các lớp tĩnh. Bạn có nghĩa là chỉ có phương thức tĩnh? Tại sao phải có một lớp thay vì một không gian tên sau đó?

1
@Joe: Chà, câu hỏi không tập trung vào C ++ như tôi đã hiểu. Trong C # hoặc Java, bạn có thể tạo các lớp tĩnh và tôi đang đề cập đến các lớp đó. Ngoài ra, như tôi đã nói, hầu hết các lớp tĩnh không phải là một giải pháp tối ưu, nhưng trong những trường hợp hiếm hoi, nó có thể hoạt động như một toàn cầu.
Michael Klement
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.