Có phải là một ý tưởng tồi để tạo một lớp sẽ chỉ có một thể hiện?


9

Có phải là thực hành / thiết kế mã hóa xấu để tạo ra một lớp sẽ chỉ được khởi tạo một lần?

Tôi có một số biến và hàm có thể được nhóm lại với nhau trong một lớp để "trông ổn" (vì thiếu mô tả tốt hơn) vì chúng có liên quan phần nào, nhưng chúng chỉ có thể là biến toàn cục và hàm toàn cục.

(Btw, tôi đang sử dụng JavaScript, AngularJS, Express, MongoDB.)


2
Có lẽ bạn cần một lớp Singleton. Tuy nhiên, đó là ý tưởng tồi để ràng buộc trong một lớp học, những thứ không liên quan đúng cách.
NeonGlow

2
Mẫu đơn là xấu. Trường hợp duy nhất đôi khi là giải pháp chính xác.
Bryan Chen

4
Cả "Singleton" hay "Single dụ" đều không phải là một giải pháp tốt để nhóm các thứ toàn cầu "chỉ liên quan đến phần nào" với nhau - khi tôi nghe một mô tả như vậy, tôi luôn coi đây là một tín hiệu cảnh báo. Nhưng để cung cấp cho bạn một câu trả lời nghiêm túc, bạn nên cho chúng tôi biết một số ví dụ về các biến và chức năng bạn sẽ nhóm lại với nhau (với tên thật và ý nghĩa của chúng).
Doc Brown

2
Mẫu Singleton không nhất thiết phải xấu. Chỉ là nó có xu hướng bị lạm dụng.
Stephen

Thậm chí về mặt kỹ thuật có thể thực thi một Singleton trong JavaScript không?
Amy Blankenship

Câu trả lời:


10

Một thể hiện duy nhất cho một lớp có ý nghĩa nếu đối tượng đại diện cho một tài nguyên, như liên kết ethernet hoặc trình quản lý tác vụ hệ điều hành chẳng hạn.

Các hàm chỉ được đặt trong một lớp nếu chúng tác động lên các biến của các thể hiện của lớp đó, nếu không thì người bảo trì sẽ bị nhầm lẫn về ý định của lớp đó.

Thông thường, một lý do chính đáng tồn tại tại sao ứng dụng của bạn có các biến toàn cục. Cố gắng tìm mục đích chung của chúng và thiết kế một lớp học xung quanh mục đích này. Nó sẽ không chỉ làm cho thiết kế rõ ràng, mà cả tâm trí của bạn nữa.


2
+1, nhưng tôi muốn thêm: "nếu mục đích chung duy nhất là các biến đó toàn cầu, tốt hơn là để chúng trong các lớp khác nhau."
Doc Brown

13

Không có vấn đề gì với việc viết một lớp chỉ kết thúc một lần.

Nếu đặt một số biến và hàm cùng nhau trong một lớp có ý nghĩa, có giá trị ngữ nghĩa hoặc làm cho mã đơn giản hơn để đọc và thao tác, thì cũng vậy, các nhà bảo trì sẽ rất biết ơn.

Điều có thể rất sai mặc dù, là để thực thi tính duy nhất từ ​​bên trong.

Nhiều người có xu hướng kích hoạt mô hình singleton ngay khi họ nghĩ rằng họ sẽ chỉ cần một ví dụ của một cái gì đó. Xem ví dụ về cách bạn chưa mô tả bối cảnh của mình một cách sâu sắc, nhưng hầu hết các câu trả lời đã đề nghị bạn sử dụng một singleton. Và sau đó thiết kế có thể bị vặn bởi vì nếu bạn cần một trường hợp khác vào một lúc nào đó vì bất kỳ lý do gì, lớp sẽ ngăn bạn thực hiện nó mà không thay đổi nhiều mã.

Vì vậy, như một kết luận, một trường hợp là tốt, nhưng hãy chắc chắn để biết liệu:

  1. bạn chỉ cần một ví dụ, hoặc
  2. bạn không thể có nhiều hơn một trường hợp (một trường hợp rất, rất hiếm trong kinh nghiệm của tôi).

1
re: # 2, tôi không nghĩ điều này là hợp lệ. Ngay cả khi đối tượng của bạn là NuclePowerStationCentralCommandControll và bạn sẽ chỉ sở hữu một nhà máy điện hạt nhân, bạn không nghiêm túc nói với tôi rằng bạn không có MockNuclePowerStationCentralCommandControll để thử nghiệm, phải không?
Phoshi

Một vấn đề nghiêm trọng hơn nhưng ít rõ ràng hơn là nếu bạn cần có một triển khai Singleton hơi khác, bạn không thể sử dụng hầu hết các ngôn ngữ, bởi vì cách bạn nhận được Singleton là trực tiếp đến nó, chứ không phải là ví dụ được thông qua .
Amy Blankenship

@Phoshi: Nếu chỉ một trong những thứ có thể tồn tại, thì mã có thể sử dụng nó mà không cần phải giữ tham chiếu. Nếu nhiều trường hợp được phép tồn tại, thì người tiêu dùng sẽ phải giữ tài liệu tham khảo cho họ. Điều này không chỉ thêm vào yêu cầu lưu trữ của đối tượng, mà người tiêu dùng sẽ không còn có thể cho rằng những người tiêu dùng khác sẽ giữ một tham chiếu đến cùng một đối tượng. Điều này thêm sự phức tạp đáng kể. Nếu sự phức tạp cuối cùng sẽ là cần thiết, tốt hơn là thêm nó sớm hơn là muộn hơn, nhưng nếu không cần thiết, tốt nhất có thể tránh.
supercat

@supercat: Việc thêm giá trị sử dụng bộ nhớ của một tham chiếu hoàn toàn không liên quan đến phần lớn các trường hợp, tôi không nghĩ đó là lý do chính đáng để tránh DI. Bạn đúng rằng bạn không thể cho rằng tất cả mọi người đều có cùng một ví dụ nữa - bạn không nên, điều đó không quan trọng. Sẽ không có vấn đề gì nếu mọi người đều có một trường hợp khác nhau miễn là ngữ nghĩa của giao diện được giữ. Dù sao, như tôi đã nói, bạn sẽ không bao giờ chỉ có một trường hợp. Bạn có ít nhất một lần nữa để thử nghiệm và bạn có thực sự muốn mạo hiểm với giả định rằng các yêu cầu của bạn sẽ không bao giờ thay đổi?
Phoshi

@Phoshi: Việc sử dụng bộ nhớ không phải là vấn đề; vấn đề là người ta thêm một cách khác trong đó các đối tượng có thể khác nhau. Ví dụ, xem xét ý nghĩa của các mục trong hai trường hợp Dictionary<TKey,KTValue>là khác biệt. Nếu tất cả các phiên bản của một Dictionary<TKey,KTValue>cá thể sử dụng giống nhau IEqualityComparer, sẽ không có sự mơ hồ, nhưng nếu họ có thể sử dụng các bộ so sánh khác nhau xác định các mối quan hệ tương đương khác nhau, tôi không biết rằng có thể xác định một cách hợp lý a.ContentsDistinctFrom(b)để luôn luôn bằng nhau b.ContentsDistinctFrom(a).
supercat

3

Có phải là thực hành / thiết kế mã hóa xấu để tạo ra một lớp sẽ chỉ được khởi tạo một lần?

Không, ngay cả khi chỉ khởi tạo một lần, bạn cần một lớp cho nó. Nhưng đừng cho rằng bạn sẽ luôn chỉ cần một ví dụ, bây giờ và trong tương lai.

Giả sử bạn tạo một trò chơi và bạn có một lớp người chơi:

class Player {
    ...
}

Bạn sẽ khởi tạo một lớp người chơi trong trò chơi của mình và luôn sử dụng cùng một đối tượng. Không có gì sai với nó.

Tuy nhiên, đừng tạo ra một Singleton từ nó! Hôm nay bạn thiết kế trò chơi của mình và nghĩ rằng "Chà, nó sẽ luôn là trò chơi một người chơi. Tôi sẽ sử dụng Singleton cho Người chơi của tôi". Nhưng trong phiên bản 2, bạn có thể muốn triển khai nhiều người chơi - và sau đó bạn đang gặp rắc rối. Bạn sẽ phải viết lại và điều chỉnh rất nhiều mã, vì thiết kế của bạn không hỗ trợ khởi tạo nhiều người chơi.

Điều tương tự là với cache. Hoặc logger. Hoặc giao diện mạng. Hôm nay, bạn nghĩ rằng bạn sẽ chỉ cần một, luôn luôn, chắc chắn. Nhưng trong phiên bản tương lai, bạn cần thêm một giây. Hãy nhận biết tình huống này.


1

Có các lớp singleton và đó là một mẫu thiết kế phổ biến.

Cũng có những lớp không bao giờ được khởi tạo. Nếu bạn có các hàm KHÔNG hoạt động trên bất kỳ trạng thái liên tục nào (ví dụ: chúng lấy đầu vào và biến đổi nó), bạn thường có thể sử dụng phương thức tĩnh. Trong một số ngôn ngữ (như Java), các phương thức tĩnh này sẽ được khai báo trong một lớp, nhưng chính lớp đó sẽ không bao giờ được khởi tạo.


1

Các mẫu singleton là phổ biến. Nó cho phép bạn thực thi một thể hiện của lớp.

Nhưng đối với một số người nó là quá phổ biến, vì vậy nó trở thành một mô hình chống. Lý do cho điều này là vì nó thực tế giống như nhà nước toàn cầu. Và bất kỳ nhà nước toàn cầu nào cũng khó chế giễu và kiểm tra và khó gỡ lỗi. Nhưng đồng thời, việc phải vượt qua một cách rõ ràng trường hợp này ở mọi nơi trong mã của bạn có thể khiến mã bị phồng lên rất nhiều. Nhưng điều này sẽ khiến bạn suy nghĩ về kiến ​​trúc thích hợp. Nếu một nhóm các lớp sử dụng một singleton này, bạn có thể thấy chúng có liên quan bằng cách nào đó và tạo ra tổ tiên chung cho chúng, điều đó sẽ gói gọn một thể hiện này cho chúng.


1

Thật tệ khi cho rằng một cái gì đó là xấu toàn cầu. Trong trường hợp của bạn, có thể đó là một ý tưởng tốt, nhưng nếu bạn đang sử dụng một ngôn ngữ không phải là OO ở cốt lõi thì có lẽ chỉ nên sử dụng các phương thức và biến lỏng lẻo và đưa chúng vào một thư viện chung mà bạn đưa vào công việc khác.


1

Bạn đang hỏi hai câu hỏi trong một.

  1. Không, không có gì xấu khi có một lớp sẽ chỉ có một thể hiện.
  2. Nhóm các biến và phương thức trong một lớp mà không có lý do thực sự cho nó là thiết kế xấu.
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.