Tại sao các biến tĩnh được coi là xấu xa?


635

Tôi là một lập trình viên Java, người mới trong thế giới doanh nghiệp. Gần đây tôi đã phát triển một ứng dụng bằng Groovy và Java. Tất cả thông qua mã tôi đã viết sử dụng khá nhiều số liệu thống kê. Tôi đã được yêu cầu bởi các lô kỹ thuật cao cấp để cắt giảm số lượng thống kê được sử dụng. Tôi đã hiểu về điều tương tự, và tôi thấy rằng nhiều lập trình viên khá chống lại việc sử dụng các biến tĩnh.

Tôi tìm thấy các biến tĩnh thuận tiện hơn để sử dụng. Và tôi cho rằng chúng cũng hiệu quả (vui lòng sửa cho tôi nếu tôi sai), bởi vì nếu tôi phải thực hiện 10.000 cuộc gọi đến một hàm trong một lớp, tôi sẽ rất vui khi làm cho phương thức tĩnh và sử dụng một cách đơn giản Class.methodCall()trên nó thay vì làm lộn xộn bộ nhớ với 10.000 trường hợp của lớp, phải không?

Hơn nữa, số liệu thống kê làm giảm sự phụ thuộc lẫn nhau vào các phần khác của mã. Họ có thể đóng vai trò là chủ sở hữu nhà nước hoàn hảo. Thêm vào đó tôi thấy rằng các thống kê được triển khai rộng rãi trong một số ngôn ngữ như SmalltalkScala . Vậy tại sao sự áp bức về thống kê này lại phổ biến trong các lập trình viên (đặc biệt là trong thế giới của Java)?

PS: xin vui lòng sửa cho tôi nếu các giả định của tôi về thống kê là sai.


43
Chỉ cần nói rằng, không có biến hoặc phương thức tĩnh trên Smalltalk hoặc Scala, chính xác là vì các phương thức và biến tĩnh chống lại các nguyên tắc OOP.
Maurício Linhares

87
Ít nhất một tuyên bố bạn đưa ra khá tò mò: "statics làm giảm sự phụ thuộc lẫn nhau vào các phần khác của mã". Nói chung họ thắt chặt các phụ thuộc. Mã nơi thực hiện cuộc gọi được liên kết rất chặt chẽ với mã được gọi. Không trừu tượng giữa, phụ thuộc trực tiếp.
Arne Đức

11
Câu hỏi hay ... nhiều hơn về lập trình viên. Câu hỏi thường gặp?
WernerCD

26
Đoạn thứ hai của bạn là về một chủ đề hoàn toàn khác, cụ thể là các phương thức tĩnh .
Paul

8
Lập trình chức năng cũng cau mày khi nhà nước toàn cầu là tốt. Nếu bạn từng (và bạn nên ) vào FP một ngày nào đó, hãy chuẩn bị để từ bỏ khái niệm về nhà nước toàn cầu.
new123456

Câu trả lời:


689

Biến tĩnh đại diện cho trạng thái toàn cầu. Thật khó để lý luận và khó kiểm tra: nếu tôi tạo một phiên bản mới của một đối tượng, tôi có thể suy luận về trạng thái mới của nó trong các thử nghiệm. Nếu tôi sử dụng mã đang sử dụng các biến tĩnh, nó có thể ở bất kỳ trạng thái nào - và bất cứ điều gì cũng có thể sửa đổi nó.

Tôi có thể tiếp tục khá lâu, nhưng khái niệm lớn hơn để suy nghĩ là phạm vi của một cái gì đó càng chặt chẽ thì càng dễ lý luận. Chúng tôi rất giỏi suy nghĩ về những điều nhỏ nhặt, nhưng thật khó để suy luận về tình trạng của một hệ thống hàng triệu dòng nếu không có tính mô đun. Điều này áp dụng cho tất cả các loại, bằng cách này - không chỉ các biến tĩnh.


57
Điều đó gần đây dường như là một đối số, cho dù mã có thể kiểm tra được hay không. Đó là một lý do khá thiếu sót. Đối số phải là 'thiết kế tốt' và thông thường thiết kế tốt có thể kiểm tra được. Nhưng không phải là cách khác: "Tôi không thể kiểm tra nó vì nó phải là thiết kế xấu." Đừng hiểu lầm tôi, tôi đồng ý với bài viết của bạn nói chung.
M Platvoet

144
@M Platvoet: Tôi sẽ nói rằng đưa ra một sự lựa chọn giữa hai thiết kế có giá trị như nhau, thì thiết kế có thể thử nghiệm là vượt trội. Có thể kiểm tra chắc chắn không đồng nghĩa với việc được thiết kế tốt, nhưng tôi hiếm khi bắt gặp những thiết kế tốt không thể kiểm chứng được và tôi nghĩ rằng chúng đủ hiếm để tôi không gặp vấn đề gì trong việc biến khả năng kiểm tra thành một chỉ số chung cho mục đích thiết kế tốt.
Jon Skeet

9
@M Platvoet - Khả năng kiểm tra ảnh hưởng đến cả khả năng bảo trì và độ tin cậy, và tôi sẽ xem xét các yếu tố chính trong chất lượng thiết kế. Chắc chắn chúng không phải là các yếu tố duy nhất, nhưng IMHO chi phí của bất kỳ mã đã cho nào là sự kết hợp giữa chu kỳ máy, chu kỳ nhà phát triển và chu kỳ người dùng. Khả năng kiểm tra đánh hai trong số ba.
Justin Morgan

5
@M Platvoet - Khả năng kiểm tra cũng có xu hướng ảnh hưởng đến khả năng sử dụng lại, vì một lớp tách rời thường dễ sử dụng lại hơn.
TrueWill

13
M Platvoet - Tôi không đồng ý với nhận xét đầu tiên của bạn ở đây. Tôi nghĩ rằng nếu một cái gì đó không thể được thử nghiệm, thì đó là thiết kế tồi; bởi vì nếu tôi không thể kiểm tra nó, tôi không thể biết rằng nó hoạt động. Bạn có mua xe không nếu nhân viên bán hàng nói với bạn "Thiết kế của mẫu này ngăn không cho thử nghiệm, vì vậy tôi không biết nếu nó thực sự chạy"? Khả năng kiểm tra là rất quan trọng đối với phần mềm (cũng như ô tô), mà DEMANDS thiết kế có thẩm quyền mà nó được đưa vào.
Dawood ibn Kareem

277

Nó không hướng đối tượng lắm: Một số lý do thống kê có thể bị một số người coi là "xấu xa" là chúng trái ngược với mô hình hướng đối tượng . Cụ thể, nó vi phạm nguyên tắc dữ liệu được gói gọn trong các đối tượng (có thể được mở rộng, ẩn thông tin, v.v.). Statics, theo cách bạn đang mô tả bằng cách sử dụng chúng, về cơ bản là sử dụng chúng như một biến toàn cục để tránh xử lý các vấn đề như phạm vi. Tuy nhiên, các biến toàn cục là một trong những đặc điểm xác định của mô hình lập trình thủ tục hoặc bắt buộc, không phải là một đặc tính của mã hướng đối tượng "tốt". Điều này không có nghĩa là mô hình thủ tục là xấu, nhưng tôi có ấn tượng rằng người giám sát của bạn hy vọng bạn sẽ viết "mã hướng đối tượng tốt" và bạn thực sự muốn viết "

Có rất nhiều vấn đề về Java khi bạn bắt đầu sử dụng các số liệu thống kê không phải lúc nào cũng rõ ràng. Ví dụ: nếu bạn có hai bản sao chương trình của mình đang chạy trong cùng một VM, chúng sẽ loại bỏ giá trị của biến tĩnh và gây rối với trạng thái của nhau? Hoặc điều gì xảy ra khi bạn mở rộng lớp, bạn có thể ghi đè thành viên tĩnh không? Có phải VM của bạn hết bộ nhớ vì bạn có số lượng thống kê điên rồ và bộ nhớ đó không thể lấy lại được cho các đối tượng cần thiết khác?

Đối tượng trọn đời: Ngoài ra, số liệu thống kê có thời gian tồn tại phù hợp với toàn bộ thời gian chạy của chương trình. Điều này có nghĩa, ngay cả khi bạn đã hoàn thành việc sử dụng lớp của mình, bộ nhớ từ tất cả các biến tĩnh đó không thể được thu gom rác. Ví dụ, nếu thay vào đó, bạn đã biến các biến của mình thành không tĩnh và trong hàm main () của bạn, bạn đã tạo một thể hiện của lớp và sau đó yêu cầu lớp của bạn thực hiện một hàm cụ thể 10.000 lần, sau khi 10.000 cuộc gọi đó được thực hiện và bạn xóa các tham chiếu của bạn đến một thể hiện duy nhất, tất cả các biến tĩnh của bạn có thể là rác được thu thập và sử dụng lại.

Ngăn chặn việc sử dụng lại nhất định: Ngoài ra, các phương thức tĩnh không thể được sử dụng để thực hiện giao diện, vì vậy các phương thức tĩnh có thể ngăn các tính năng hướng đối tượng nhất định không sử dụng được.

Sự lựa chọn khác: Nếu hiệu quả là mối quan tâm chính của bạn, có thể có những cách khác tốt hơn để giải quyết vấn đề tốc độ hơn là chỉ xem xét lợi thế của việc gọi thường nhanh hơn tạo. Xem xét liệu các sửa đổi thoáng qua hoặc dễ bay hơi là cần thiết ở bất cứ đâu. Để duy trì khả năng được nội tuyến, một phương thức có thể được đánh dấu là cuối cùng thay vì tĩnh. Các tham số phương thức và các biến khác có thể được đánh dấu cuối cùng để cho phép tối ưu hóa trình biên dịch nhất định dựa trên các giả định về những gì có thể thay đổi các biến đó. Một đối tượng thể hiện có thể được sử dụng lại nhiều lần thay vì tạo một thể hiện mới mỗi lần. Có thể có các công tắc tối ưu hóa trình biên dịch nên được bật cho ứng dụng nói chung. Có lẽ, thiết kế nên được thiết lập để 10.000 lần chạy có thể đa luồng và tận dụng các lõi đa xử lý. Nếu tính di động là '

Nếu vì lý do nào đó bạn không muốn nhiều bản sao của một đối tượng, mẫu thiết kế singleton, có các ưu điểm so với các đối tượng tĩnh, chẳng hạn như an toàn luồng (giả sử singleton của bạn được mã hóa tốt), cho phép khởi tạo lười biếng, đảm bảo đối tượng đã được khởi tạo đúng cách khi được sử dụng, phân loại phụ, lợi thế trong kiểm tra và tái cấu trúc mã của bạn, không đề cập đến, nếu tại một thời điểm nào đó, bạn thay đổi suy nghĩ về việc chỉ muốn một phiên bản của một đối tượng thì việc loại bỏ mã để ngăn chặn các trường hợp trùng lặp sẽ dễ dàng hơn so với việc tái cấu trúc tất cả mã biến tĩnh của bạn để sử dụng các biến thể. Tôi đã phải làm điều đó trước đây, điều đó không thú vị và cuối cùng bạn phải chỉnh sửa nhiều lớp hơn, điều này làm tăng nguy cơ giới thiệu các lỗi mới ... tốt hơn rất nhiều khi lần đầu tiên thiết lập mọi thứ "đúng", ngay cả khi nó có vẻ như có nhược điểm của nó. Cho tôi, công việc cần thiết nếu bạn quyết định trên con đường bạn cần nhiều bản sao của một cái gì đó có lẽ là một trong những lý do thuyết phục nhất để sử dụng thống kê càng ít càng tốt. Và do đó tôi cũng không đồng ý với tuyên bố của bạn rằng statics làm giảm sự phụ thuộc lẫn nhau, tôi nghĩ rằng bạn sẽ kết thúc với mã được ghép nhiều hơn nếu bạn có nhiều số liệu thống kê có thể được truy cập trực tiếp, thay vì một đối tượng "biết cách làm một cái gì đó "trên chính nó.


11
Tôi thích câu trả lời của bạn, tôi nghĩ rằng nó tập trung vào sự đánh đổi đúng đắn để xem xét xung quanh các thống kê hơn là một số những người thừa kế đỏ như đồng thời và phạm vi. Và +1 cho singletons, một câu hỏi tốt hơn thực sự có thể là khi sử dụng các biến / phương thức tĩnh so với singletons ...
studgeek

2
Mặc dù bản thân singleton có thể an toàn cho luồng (ví dụ: bằng cách sử dụng synchronizedcác phương thức), điều đó không có nghĩa là mã gọi không có điều kiện chủng tộc liên quan đến trạng thái của singleton.
André Caron

8
Ngoài ra, số liệu thống kê không chống lại mô hình OOP. Rất nhiều người cuồng OOP sẽ nói với bạn rằng lớp là một đối tượng và phương thức tĩnh là một phương thức của đối tượng lớp, chứ không phải là các thể hiện của nó. Hiện tượng này ít xuất hiện trong Java. Các ngôn ngữ khác, chẳng hạn như Python cho phép bạn sử dụng các lớp làm biến và bạn có thể truy cập các phương thức tĩnh làm phương thức của đối tượng đó.
André Caron

4
Dòng cuối cùng của đoạn thứ ba nên đọc, tất cả các biến không tĩnh của bạn , nếu tôi không nhầm.
Steve

1
Object Lifetime, là một điểm rất quan trọng mà @jessica đã đề cập.
Abhidemon

93

Cái ác là một thuật ngữ chủ quan.

Bạn không kiểm soát các thống kê về mặt sáng tạo và phá hủy. Họ sống theo lệnh của chương trình tải và dỡ hàng.

Vì các thống kê sống trong một không gian, nên tất cả các luồng muốn sử dụng chúng phải thông qua kiểm soát truy cập mà bạn phải quản lý. Điều này có nghĩa là các chương trình được kết hợp nhiều hơn và sự thay đổi này khó dự kiến ​​và quản lý hơn (như J Skeet nói). Điều này dẫn đến các vấn đề về cách ly tác động thay đổi và do đó ảnh hưởng đến cách kiểm tra được quản lý.

Đây là hai vấn đề chính tôi có với họ.


59

Không. Các quốc gia toàn cầu không xấu xa. Nhưng chúng tôi phải xem mã của bạn để xem bạn đã sử dụng đúng cách chưa. Hoàn toàn có khả năng một người mới lạm dụng các quốc gia toàn cầu; giống như anh ta sẽ lạm dụng mọi tính năng ngôn ngữ.

Nhà nước toàn cầu là cần thiết tuyệt đối. Chúng ta không thể tránh các quốc gia toàn cầu. Chúng ta không thể tránh lý luận về các quốc gia toàn cầu. - Nếu chúng tôi quan tâm để hiểu ngữ nghĩa ứng dụng của chúng tôi.

Những người cố gắng thoát khỏi các quốc gia toàn cầu vì lợi ích của nó, chắc chắn sẽ kết thúc với một hệ thống phức tạp hơn nhiều - và các quốc gia toàn cầu vẫn còn đó, được ngụy trang một cách khéo léo / ngớ ngẩn dưới nhiều tầng lớp; và chúng ta vẫn phải suy luận về các quốc gia toàn cầu, sau khi hủy bỏ tất cả các điều khoản.

Giống như những người mùa xuân, những người xa hoa tuyên bố các trạng thái toàn cầu trong xml và nghĩ bằng cách nào đó nó vượt trội.

@Jon Skeet if I create a new instance of an objectbây giờ bạn có hai điều cần lý do - trạng thái bên trong đối tượng và trạng thái của môi trường lưu trữ đối tượng.


10
"Tôi có hai điều để lý do". Không, nếu tôi làm bài kiểm tra của mình chỉ phụ thuộc vào trạng thái đối tượng. Cái nào dễ hơn, tôi có ít nhà nước toàn cầu hơn.
DJClayworth

2
Việc tiêm phụ thuộc không liên quan gì đến tầm nhìn toàn cầu hoặc toàn cầu - ngay cả bản thân container cũng không phải là toàn cầu. So với mã "thông thường", điều duy nhất bổ sung mà một đối tượng được quản lý vùng chứa có thể nhìn thấy là đối với chính vùng chứa. Trên thực tế, DI được sử dụng rất phổ biến để tránh Mẫu Singleton.
Floegipoky

31

Có 2 vấn đề chính với các biến tĩnh:

  • An toàn luồng - tài nguyên tĩnh theo định nghĩa không an toàn luồng
  • Mã tiềm ẩn - Bạn không biết khi nào một biến tĩnh được khởi tạo và liệu nó có được khởi tạo trước một biến tĩnh khác hay không

Tôi nghĩ Jon Skeet đã giới thiệu cùng một bình luận mà bạn đã đăng.
RG-3

13
Tôi không nhận được điểm An toàn luồng, tôi nghĩ rằng không có gì là an toàn cho luồng trừ khi bạn làm như vậy. Điều này dường như không liên quan đến những thứ tĩnh, xin vui lòng sửa cho tôi nếu tôi thiếu một cái gì đó.
Zmaster

1
@Zmaster - Mặc dù đúng là an toàn luồng không phải là vấn đề dành riêng cho các biến tĩnh, bởi vì theo định nghĩa của chúng, chúng phải được gọi từ và bởi các bối cảnh khác nhau, chúng dễ bị cắt tỉa hơn
sternr

2
@sternr Tôi hiểu ý của bạn là gì, sự kiện nếu "các bối cảnh khác nhau" không nhất thiết phải bằng "các chủ đề khác nhau". Nhưng sự thật là nhu cầu an toàn luồng thường được tính đến với các tài nguyên tĩnh. Bạn nên xem xét làm rõ câu.
Zmaster

Có chủ đề hợp lệ sử dụng an toàn tài nguyên tĩnh, ví dụ. private logger logger LOG = Logger.getLogger (Foo. class); nguyên tử tĩnh cuối cùng AtomicInteger x = new AtomicInteger (0); Theo tôi hiểu, việc gán các tài nguyên tĩnh như thế này được đảm bảo an toàn luồng bởi trình nạp lớp. Ví dụ Logger là hoặc không an toàn luồng độc lập với nơi bạn gán con trỏ cho nó. Giữ trạng thái trong thống kê có lẽ không phải là một ý tưởng tốt, nhưng không có lý do gì nó không an toàn cho chủ đề.
teknopaul

29

Nếu bạn đang sử dụng từ khóa 'tĩnh' mà không có từ khóa 'cuối cùng', đây sẽ là một tín hiệu để xem xét cẩn thận thiết kế của bạn. Ngay cả sự hiện diện của 'trận chung kết' cũng không phải là đường chuyền miễn phí, vì một đối tượng tĩnh cuối có thể thay đổi có thể nguy hiểm như vậy.

Tôi sẽ ước tính ở đâu đó khoảng 85% thời gian tôi thấy một "tĩnh" mà không có "trận chung kết", đó là SAI. Thông thường, tôi sẽ tìm cách giải quyết lạ để che giấu hoặc che giấu những vấn đề này.

Vui lòng không tạo đột biến tĩnh. Đặc biệt là Bộ sưu tập. Nói chung, Bộ sưu tập nên được khởi tạo khi đối tượng chứa của chúng được khởi tạo và nên được thiết kế sao cho chúng được đặt lại hoặc quên khi đối tượng chứa của chúng bị lãng quên.

Sử dụng statics có thể tạo ra những lỗi rất tinh vi sẽ gây ra những ngày đau khổ cho các kỹ sư. Tôi biết, bởi vì tôi đã tạo và săn những con bọ này.

Nếu bạn muốn biết thêm chi tiết, vui lòng đọc trên giáo dục

Tại sao không sử dụng Statics?

Có nhiều vấn đề với thống kê, bao gồm viết và thực hiện các bài kiểm tra, cũng như các lỗi tinh vi không rõ ràng ngay lập tức.

Mã dựa trên các đối tượng tĩnh không thể dễ dàng được kiểm tra đơn vị và các thống kê không thể dễ dàng bị chế giễu (thông thường).

Nếu bạn sử dụng statics, không thể hoán đổi việc thực hiện lớp để kiểm tra các thành phần cấp cao hơn. Ví dụ, hãy tưởng tượng một CustomerDAO tĩnh trả về các đối tượng Khách hàng mà nó tải từ cơ sở dữ liệu. Bây giờ tôi có một lớp Trình khách hàng, cần truy cập vào một số đối tượng Khách hàng. Nếu CustomerDAO là tĩnh, tôi không thể viết bài kiểm tra cho Bộ lọc khách hàng mà không khởi tạo cơ sở dữ liệu của mình trước đó và điền thông tin hữu ích.

Và cơ sở dữ liệu dân số và khởi tạo mất một thời gian dài. Và theo kinh nghiệm của tôi, khung khởi tạo DB của bạn sẽ thay đổi theo thời gian, có nghĩa là dữ liệu sẽ biến đổi và các thử nghiệm có thể bị hỏng. IE, hãy tưởng tượng Khách hàng 1 từng là VIP, nhưng khung khởi tạo DB đã thay đổi và bây giờ Khách hàng 1 không còn là VIP, nhưng thử nghiệm của bạn đã được mã hóa cứng để tải Khách hàng 1

Một cách tiếp cận tốt hơn là khởi tạo một CustomerDAO và chuyển nó vào Bộ lọc khách hàng khi nó được xây dựng. (Một cách tiếp cận thậm chí tốt hơn sẽ là sử dụng Spring hoặc khung Inversion of Control khác.

Khi bạn thực hiện việc này, bạn có thể nhanh chóng mô phỏng hoặc loại bỏ một DAO thay thế trong CustomerFilterTest của bạn, cho phép bạn có nhiều quyền kiểm soát hơn đối với bài kiểm tra,

Nếu không có DAO tĩnh, thử nghiệm sẽ nhanh hơn (không khởi tạo db) và đáng tin cậy hơn (vì sẽ không thất bại khi mã khởi tạo db thay đổi). Ví dụ: trong trường hợp này, đảm bảo Khách hàng 1 luôn luôn là VIP, theo như thử nghiệm có liên quan.

Kiểm tra thực hiện

Số liệu thống kê gây ra sự cố thực sự khi chạy các bộ kiểm tra đơn vị cùng nhau (ví dụ: với máy chủ Tích hợp liên tục của bạn). Tưởng tượng một bản đồ tĩnh của các đối tượng Ổ cắm mạng vẫn mở từ thử nghiệm này sang thử nghiệm khác. Thử nghiệm đầu tiên có thể mở một Ổ cắm trên cổng 8080, nhưng bạn đã quên xóa Bản đồ khi thử nghiệm bị phá hỏng. Bây giờ khi thử nghiệm thứ hai khởi chạy, nó có khả năng bị sập khi cố gắng tạo một Ổ cắm mới cho cổng 8080, do cổng vẫn bị chiếm dụng. Cũng hãy tưởng tượng rằng các tham chiếu socket trong Bộ sưu tập tĩnh của bạn không bị xóa và (ngoại trừ WeakHashMap) không bao giờ đủ điều kiện để được thu gom rác, gây rò rỉ bộ nhớ.

Đây là một ví dụ khái quát quá mức, nhưng trong các hệ thống lớn, vấn đề này xảy ra TẤT CẢ THỜI GIAN. Mọi người không nghĩ về các thử nghiệm đơn vị bắt đầu và dừng phần mềm của họ liên tục trong cùng một JVM, nhưng đó là một thử nghiệm tốt về thiết kế phần mềm của bạn và nếu bạn có nguyện vọng về tính sẵn sàng cao, đó là điều bạn cần lưu ý.

Những vấn đề này thường phát sinh với các đối tượng khung, ví dụ: quyền truy cập DB, bộ nhớ đệm, nhắn tin và các lớp ghi nhật ký của bạn. Nếu bạn đang sử dụng Java EE hoặc một số khung công tác tốt nhất, họ có thể quản lý rất nhiều điều này cho bạn, nhưng nếu như tôi, bạn đang xử lý một hệ thống cũ, bạn có thể có rất nhiều khung tùy chỉnh để truy cập các lớp này.

Nếu cấu hình hệ thống áp dụng cho các thành phần khung này thay đổi giữa các thử nghiệm đơn vị và khung thử nghiệm đơn vị không phá hủy và xây dựng lại các thành phần, các thay đổi này không thể có hiệu lực và khi thử nghiệm dựa vào các thay đổi đó, chúng sẽ thất bại .

Ngay cả các thành phần phi khung cũng phải chịu vấn đề này. Hãy tưởng tượng một bản đồ tĩnh được gọi là OpenOrder. Bạn viết một bài kiểm tra tạo ra một vài đơn đặt hàng mở và kiểm tra để đảm bảo tất cả chúng đều ở trạng thái phù hợp, sau đó bài kiểm tra kết thúc. Một nhà phát triển khác viết một bài kiểm tra thứ hai trong đó đặt các đơn đặt hàng mà họ cần vào bản đồ OpenOrder, sau đó xác nhận số lượng đơn đặt hàng là chính xác. Chạy riêng lẻ, các bài kiểm tra này sẽ vượt qua, nhưng khi chạy cùng nhau trong một bộ, chúng sẽ thất bại.

Tệ hơn, thất bại có thể dựa trên thứ tự các bài kiểm tra đã được chạy.

Trong trường hợp này, bằng cách tránh các số liệu thống kê, bạn tránh được rủi ro lưu giữ dữ liệu trong các trường hợp kiểm tra, đảm bảo độ tin cậy kiểm tra tốt hơn.

Lỗi tinh tế

Nếu bạn làm việc trong môi trường có tính sẵn sàng cao hoặc bất cứ nơi nào có thể bắt đầu và dừng các luồng, thì mối quan tâm tương tự được đề cập ở trên với các bộ kiểm thử đơn vị cũng có thể áp dụng khi mã của bạn đang chạy trên sản xuất.

Khi xử lý các luồng, thay vì sử dụng một đối tượng tĩnh để lưu trữ dữ liệu, tốt hơn là sử dụng một đối tượng được khởi tạo trong giai đoạn khởi động của luồng. Theo cách này, mỗi khi luồng được khởi động, một phiên bản mới của đối tượng (với cấu hình có khả năng mới) được tạo và bạn tránh dữ liệu từ một phiên bản của luồng chảy qua phiên bản tiếp theo.

Khi một luồng chết, một đối tượng tĩnh không được thiết lập lại hoặc rác được thu thập. Hãy tưởng tượng bạn có một chuỗi có tên là Email EmailCustomers 'và khi nó bắt đầu, nó sẽ tạo ra một bộ sưu tập Chuỗi tĩnh với một danh sách các địa chỉ email, sau đó bắt đầu gửi email cho từng địa chỉ. Hãy nói rằng luồng bị gián đoạn hoặc bị hủy bằng cách nào đó, vì vậy khung sẵn sàng cao của bạn khởi động lại luồng. Sau đó, khi chủ đề khởi động, nó tải lại danh sách khách hàng. Nhưng vì bộ sưu tập là tĩnh, nên nó có thể giữ lại danh sách các địa chỉ email từ bộ sưu tập trước đó. Bây giờ một số khách hàng có thể nhận được email trùng lặp.

Bên cạnh: Chung kết tĩnh

Việc sử dụng của static static cuối cùng là một cách hiệu quả tương đương với Java của C #define, mặc dù có những khác biệt về triển khai kỹ thuật. Bộ xử lý trước AC / C ++ #define bị tráo đổi mã trước khi biên dịch. Một bản cuối cùng của Java Java sẽ kết thúc cư dân bộ nhớ trên ngăn xếp. Theo cách đó, nó tương tự như một biến const const tĩnh trong C ++ so với biến #define.

Tóm lược

Tôi hy vọng điều này sẽ giúp giải thích một vài lý do cơ bản tại sao số liệu thống kê có vấn đề. Nếu bạn đang sử dụng một khung công tác Java hiện đại như Java EE hoặc Spring, v.v., bạn có thể không gặp phải nhiều tình huống này, nhưng nếu bạn đang làm việc với một khối mã kế thừa lớn, chúng có thể trở nên thường xuyên hơn.


15

Vì không ai * đã đề cập đến nó: đồng thời. Biến tĩnh có thể làm bạn ngạc nhiên nếu bạn có nhiều luồng đọc và ghi vào biến tĩnh. Điều này là phổ biến trong các ứng dụng web (ví dụ: ASP.NET) và nó có thể gây ra một số lỗi khá điên rồ. Ví dụ: nếu bạn có một biến tĩnh được cập nhật bởi một trang và trang được hai người yêu cầu tại "gần như cùng một lúc", một người dùng có thể nhận được kết quả mà người dùng khác mong đợi hoặc tệ hơn.

statics làm giảm sự phụ thuộc lẫn nhau vào các phần khác của mã. Họ có thể đóng vai trò là chủ sở hữu nhà nước hoàn hảo

Tôi hy vọng bạn đã chuẩn bị để sử dụng ổ khóa và đối phó với sự tranh chấp.

* Thật ra, Preet Sangha đã đề cập đến nó.


5
Các biến sơ thẩm không có lợi thế về an toàn luồng so với thống kê, chúng đều là các biến không được bảo vệ. Thay vào đó, tất cả đều thuộc về cách bạn bảo vệ mã truy cập các biến đó.
studgeek

2
Tôi hoàn toàn không đưa ra yêu sách đó, nhưng vì mục đích thảo luận: chia ly là một hình thức bảo vệ. Trạng thái chủ đề được tách ra; nhà nước toàn cầu thì không . Một biến đối tượng không cần bảo vệ trừ khi nó được chia sẻ rõ ràng giữa các luồng; một biến tĩnh luôn được chia sẻ bởi tất cả các luồng trong tiến trình.
Justin M. Keyes

Tôi muốn các biến tĩnh của luồng là một khái niệm hạng nhất, vì chúng có thể rất hữu ích để cung cấp thông tin một cách an toàn cho một cuộc gọi chương trình con được gói mà không phải truyền thông tin đó qua mọi lớp bọc. Ví dụ: nếu một đối tượng có các phương thức để hiển thị nó vào bối cảnh đồ họa hiện tại của luồng và có các phương thức để lưu / khôi phục bối cảnh đồ họa hiện tại, thì việc sử dụng chúng thường có thể sạch hơn là phải chuyển ngữ cảnh đồ họa qua mọi lệnh gọi phương thức.
supercat

15

Tóm tắt một vài Ưu điểm & Nhược điểm cơ bản của việc sử dụng các phương thức tĩnh trong Java:

Ưu điểm:

  1. Có thể truy cập toàn cầu tức là không bị ràng buộc với bất kỳ đối tượng cụ thể nào.
  2. Một ví dụ cho mỗi JVM.
  3. Có thể được truy cập bằng cách sử dụng tên lớp (Không yêu cầu đối tượng).
  4. Chứa một giá trị duy nhất áp dụng cho tất cả các trường hợp.
  5. Tải lên khi khởi động JVM và chết khi JVM tắt.
  6. Họ không sửa đổi trạng thái của Object.

Nhược điểm:

  1. Các thành viên tĩnh luôn là một phần của bộ nhớ cho dù chúng có được sử dụng hay không.
  2. Bạn không thể kiểm soát việc tạo và phá hủy biến tĩnh. Sử dụng chúng đã được tạo khi tải chương trình và hủy khi chương trình dỡ tải (hoặc khi JVM tắt).
  3. Bạn có thể làm cho chủ đề thống kê an toàn bằng cách sử dụng đồng bộ hóa nhưng bạn cần thêm một số nỗ lực.
  4. Nếu một luồng thay đổi giá trị của một biến tĩnh có thể phá vỡ chức năng của các luồng khác.
  5. Bạn phải biết tĩnh tĩnh trước khi sử dụng nó.
  6. Bạn không thể ghi đè các phương thức tĩnh.
  7. Tuần tự hóa không hoạt động tốt với họ.
  8. Họ không tham gia vào đa hình thời gian chạy.
  9. Có một vấn đề về bộ nhớ (ở một mức độ nào đó nhưng tôi đoán không nhiều) nếu một số lượng lớn các biến / phương thức tĩnh được sử dụng. Bởi vì chúng sẽ không được thu gom rác cho đến khi chương trình kết thúc.
  10. Phương pháp tĩnh cũng khó kiểm tra.

Các nhược điểm 6, 7, 8 và 10 là nhược điểm của các ngôn ngữ / khung được sử dụng và không phải là nhược điểm của các biến tĩnh nói chung. Những nhược điểm 1, 4 và 5 cũng tồn tại đối với các giải pháp khác, như một số mẫu đơn được cung cấp bởi một số khung. (Tôi đã không bỏ phiếu cho câu trả lời, vì tôi đồng ý phần còn lại và đó là một bộ sưu tập đẹp.)
peterh - Tái lập Monica

13

nếu tôi phải thực hiện 10.000 cuộc gọi đến một hàm trong một lớp, tôi sẽ rất vui khi làm cho phương thức tĩnh và sử dụng một lớp đơn giản.methodCall () trên nó thay vì làm lộn xộn bộ nhớ với 10.000 thể hiện của lớp, phải không?

Bạn phải cân bằng nhu cầu đóng gói dữ liệu vào một đối tượng với trạng thái, so với nhu cầu đơn giản là tính toán kết quả của hàm trên một số dữ liệu.

Hơn nữa, số liệu thống kê làm giảm sự phụ thuộc lẫn nhau vào các phần khác của mã.

Đóng gói cũng vậy. Trong các ứng dụng lớn, statics có xu hướng tạo mã spaghetti và không dễ dàng cho phép tái cấu trúc hoặc thử nghiệm.

Các câu trả lời khác cũng cung cấp lý do chính đáng chống lại việc sử dụng quá nhiều số liệu thống kê.


13

Các biến tĩnh thường được coi là xấu vì chúng đại diện cho trạng thái toàn cầu và do đó khó lý luận hơn nhiều. Cụ thể, họ phá vỡ các giả định của lập trình hướng đối tượng. Trong lập trình hướng đối tượng, mỗi đối tượng có trạng thái riêng, được biểu thị bằng các biến thể hiện (không tĩnh). Các biến tĩnh biểu thị trạng thái qua các trường hợp có thể khó kiểm tra đơn vị hơn nhiều. Điều này chủ yếu là do khó khăn hơn trong việc cô lập các thay đổi đối với các biến tĩnh thành một thử nghiệm duy nhất.

Điều đó đang được nói, điều quan trọng là phải phân biệt giữa các biến tĩnh thông thường (thường được coi là xấu) và các biến tĩnh cuối cùng (hằng số AKA; không quá tệ).


4
"Biến tĩnh đại diện cho trạng thái trên các lớp" ... Tôi nghĩ bạn có nghĩa là "biến tĩnh đại diện cho trạng thái qua các thể hiện"? +1 cho "hằng số AKA tĩnh cuối cùng, không quá tệ". Vì giá trị không thể thay đổi, bất cứ điều gì phụ thuộc vào nó tại một thời điểm không thể thay đổi hoàn toàn hành vi của nó sau đó - giá trị là như nhau.
Jared Updike

"Biến tĩnh biểu thị trạng thái qua các thể hiện" là cách tốt hơn nhiều để nêu rõ trạng thái. Tôi đã chỉnh sửa câu trả lời của mình.
Jack Edmonds

9

Theo tôi thì hầu như không bao giờ về hiệu suất, đó là về thiết kế. Tôi không coi việc sử dụng các phương thức tĩnh là sai khi sử dụng các biến tĩnh (nhưng tôi đoán bạn thực sự đang nói về các cuộc gọi phương thức).

Nó chỉ đơn giản là về cách cô lập logic và cung cấp cho nó một nơi tốt. Đôi khi điều đó biện minh cho việc sử dụng các phương thức tĩnh java.lang.Mathlà một ví dụ tốt. Tôi nghĩ rằng khi bạn đặt tên cho hầu hết các lớp học của bạn XxxUtilhoặc Xxxhelperbạn nên xem xét lại thiết kế của mình.


3
Phương pháp tĩnh không có tác dụng phụ là IMO hoàn toàn tốt. Nhưng trạng thái đột biến toàn cầu hiếm khi là và tôi giải thích OP là nói về trạng thái toàn cầu.
CodeInChaos

1
@CodeInChaos hoàn toàn đồng ý. Tôi thấy OP không hoàn toàn rõ ràng về sự khác biệt giữa các phương thức tĩnh và vars.
M Platvoet

8

Tôi vừa tóm tắt một số điểm được đưa ra trong các câu trả lời. Nếu bạn tìm thấy bất cứ điều gì sai xin vui lòng sửa nó.

Chia tỷ lệ: Chúng tôi có chính xác một thể hiện của một biến tĩnh cho mỗi JVM. Giả sử chúng tôi đang phát triển một hệ thống quản lý thư viện và chúng tôi quyết định đặt tên của cuốn sách là một biến tĩnh vì chỉ có một cho mỗi cuốn sách. Nhưng nếu hệ thống phát triển và chúng tôi đang sử dụng nhiều JVM thì chúng tôi không có cách nào để tìm ra cuốn sách nào chúng tôi đang xử lý?

An toàn luồng : Cả hai biến đối tượng và biến tĩnh cần được kiểm soát khi được sử dụng trong môi trường đa luồng. Nhưng trong trường hợp một biến đối tượng, nó không cần bảo vệ trừ khi nó được chia sẻ rõ ràng giữa các luồng nhưng trong trường hợp biến tĩnh, nó luôn được chia sẻ bởi tất cả các luồng trong tiến trình.

Kiểm tra: Mặc dù thiết kế có thể kiểm tra không bằng thiết kế tốt nhưng chúng ta sẽ hiếm khi quan sát một thiết kế tốt không thể kiểm tra được. Vì các biến tĩnh đại diện cho trạng thái toàn cầu và rất khó để kiểm tra chúng.

Lý do về trạng thái: Nếu tôi tạo một thể hiện mới của một lớp thì chúng ta có thể suy luận về trạng thái của thể hiện này nhưng nếu nó có các biến tĩnh thì nó có thể ở bất kỳ trạng thái nào. Tại sao? Bởi vì có thể biến tĩnh đã được sửa đổi bởi một số thể hiện khác nhau khi biến tĩnh được chia sẻ giữa các thể hiện.

Tuần tự hóa: Tuần tự hóa cũng không hoạt động tốt với họ.

Tạo và hủy: Không thể kiểm soát việc tạo và hủy các biến tĩnh. Thông thường chúng được tạo và hủy trong thời gian tải và dỡ chương trình. Điều đó có nghĩa là chúng không tốt cho việc quản lý bộ nhớ và cũng tăng thêm thời gian khởi tạo khi khởi động.

Nhưng nếu chúng ta thực sự cần chúng thì sao?

Nhưng đôi khi chúng ta có thể có một nhu cầu thực sự của họ. Nếu chúng tôi thực sự cảm thấy sự cần thiết của nhiều biến tĩnh được chia sẻ trên ứng dụng thì một tùy chọn là sử dụng mẫu Singleton Design sẽ có tất cả các biến này. Hoặc chúng ta có thể tạo một số đối tượng sẽ có các biến tĩnh này và có thể được truyền xung quanh.

Ngoài ra, nếu biến tĩnh được đánh dấu cuối cùng, nó sẽ trở thành hằng số và giá trị được gán cho nó một lần không thể thay đổi. Nó có nghĩa là nó sẽ cứu chúng ta khỏi tất cả các vấn đề chúng ta gặp phải do tính đột biến của nó.


7

Có vẻ như tôi đang hỏi về các biến tĩnh nhưng bạn cũng chỉ ra các phương thức tĩnh trong các ví dụ của bạn.

Các biến tĩnh không phải là xấu - chúng có sự chấp nhận của nó như các biến toàn cục như hằng số trong hầu hết các trường hợp kết hợp với công cụ sửa đổi cuối cùng, nhưng như đã nói, đừng lạm dụng chúng.

Phương thức tĩnh aka phương thức tiện ích. Nói chung không phải là một thực hành xấu khi sử dụng chúng nhưng mối quan tâm lớn là chúng có thể cản trở việc kiểm tra.

Như một ví dụ về dự án java tuyệt vời sử dụng nhiều số liệu thống kê và thực hiện đúng cách, vui lòng xem Play! khuôn khổ . Ngoài ra còn có thảo luận về nó trong SO.

Các biến / phương thức tĩnh kết hợp với nhập tĩnh cũng được sử dụng rộng rãi trong các thư viện tạo điều kiện cho lập trình khai báo trong java như: làm cho nó dễ dàng hoặc Hamcrest . Sẽ không thể thực hiện được nếu không có nhiều biến và phương thức tĩnh.

Vì vậy, các biến tĩnh (và phương thức) là tốt nhưng sử dụng chúng một cách khôn ngoan!


6

Các biến tĩnh quan trọng nhất tạo ra vấn đề về bảo mật dữ liệu (bất kỳ lúc nào thay đổi, bất kỳ ai cũng có thể thay đổi, truy cập trực tiếp mà không cần đối tượng, v.v.)

Để biết thêm thông tin đọc Cảm ơn này .


6

Có thể gợi ý rằng trong hầu hết các trường hợp bạn sử dụng biến tĩnh, bạn thực sự muốn sử dụng mẫu singleton .

Vấn đề với các quốc gia toàn cầu là đôi khi điều có ý nghĩa như toàn cầu trong bối cảnh đơn giản hơn, cần phải linh hoạt hơn một chút trong bối cảnh thực tế và đây là lúc mô hình singleton trở nên hữu ích.


5

Còn một lý do nữa: mong manh.

Nếu bạn có một lớp, hầu hết mọi người mong đợi có thể tạo nó và sử dụng nó theo ý muốn.

Bạn có thể ghi lại nó không phải là trường hợp, hoặc bảo vệ chống lại nó (mẫu đơn / nhà máy) - nhưng đó là công việc bổ sung, và do đó phải trả thêm chi phí. Thậm chí sau đó, trong một công ty lớn, rất có thể ai đó sẽ cố gắng sử dụng lớp của bạn mà không chú ý đầy đủ đến tất cả các bình luận tốt đẹp hoặc nhà máy.

Nếu bạn đang sử dụng biến tĩnh rất nhiều, nó sẽ bị hỏng. Lỗi là đắt tiền.

Giữa cải thiện hiệu suất 0,0001% và sự mạnh mẽ để thay đổi bởi các nhà phát triển có khả năng không biết gì, trong nhiều trường hợp, sự mạnh mẽ là lựa chọn tốt.


4

Tôi tìm thấy các biến tĩnh thuận tiện hơn để sử dụng. Và tôi cho rằng chúng cũng hiệu quả (Vui lòng sửa cho tôi nếu tôi sai) bởi vì nếu tôi phải thực hiện 10.000 cuộc gọi đến một hàm trong một lớp, tôi sẽ rất vui khi làm cho phương thức tĩnh và sử dụng một lớp đơn giản.methodCall () trên đó thay vì làm lộn xộn bộ nhớ với 10.000 thể hiện của lớp, phải không?

Tôi thấy những gì bạn nghĩ, nhưng một mẫu Singleton đơn giản sẽ làm tương tự mà không cần phải khởi tạo 10 000 đối tượng.

phương thức tĩnh có thể được sử dụng, nhưng chỉ cho các hàm có liên quan đến miền đối tượng và không cần hoặc sử dụng các thuộc tính bên trong của đối tượng.

Ví dụ:

public class WaterContainer {
    private int size;
    private int brand;
    ...etc

    public static int convertToGallon(int liters)...

    public static int convertToLiters(int gallon)...

}

Một singleton cổ điển (tức là một cái được truy cập bởi Class.Instance) hầu như không tốt hơn một biến tĩnh. Nó hơi khó kiểm tra hơn, nhưng vẫn tệ hơn nhiều so với các thiết kế mà bạn chỉ tình cờ tạo một thể hiện duy nhất thay vì xây dựng mã của mình theo giả định chỉ có một.
CodeInChaos

Không chắc tôi hiểu bình luận của bạn! tôi đã trả lời OP về những gì anh ấy đã in nghiêng về việc kích hoạt 10.000 vật thể. Tôi không hiểu tại sao bạn so sánh một biến đơn và một biến tĩnh? Điều tôi hiểu từ những gì bạn viết là Singleton có thiết kế tồi ...! Tôi đoán tôi đã hiểu nhầm bạn, vì Spring Framework tạo mặc định tất cả các hạt Singleton ;-)
Cygnusx1

Một singleton cổ điển (có Class.Instance) mang trạng thái đột biến là thiết kế xấu IMO. Trong trường hợp đó, tôi rất thích một thiết kế trong đó tôi nhận được các singletons mà tôi cần sử dụng được chuyển dưới dạng tham số vào lớp sử dụng chúng (thường là với sự trợ giúp của DI). Các singletons cổ điển bất biến là IMO tốt.
CodeInChaos

@ Cygnusx1 Trong trường hợp không rõ lý do tại sao một singleton Class (một singleton mà lớp đảm bảo là một bản sao) không thể kiểm tra dễ dàng, nó kết hợp chặt chẽ sự tồn tại của lớp với vòng đời của chương trình. Để kiểm tra nó, bạn phải tuân thủ việc khởi chạy và tắt máy của chương trình, thường có tác dụng phụ không quan trọng để kiểm tra lớp. Nếu nó có hiệu quả như singleton (một bản sao trong chương trình, nhưng không thực thi theo cách khác), bạn có thể tạo nhiều bản sao tại thời điểm kiểm tra mà không cần chương trình, xác minh rằng hành vi trên lớp là điều cần thiết cho mỗi kịch bản kiểm tra.
Edwin Buck

4

Vấn đề 'Thống kê là xấu xa' là một vấn đề về nhà nước toàn cầu. Thời gian thích hợp để một biến là tĩnh, là nếu nó không bao giờ có nhiều hơn một trạng thái; Các công cụ IE có thể được truy cập bởi toàn bộ khung công tác và luôn trả về cùng kết quả cho các cuộc gọi phương thức tương tự không bao giờ là 'xấu xa' như thống kê. Theo nhận xét của bạn:

Tôi tìm thấy các biến tĩnh thuận tiện hơn để sử dụng. Và tôi cho rằng chúng cũng hiệu quả

Statics là sự lựa chọn lý tưởng và hiệu quả cho các biến / lớp không bao giờ thay đổi .

Vấn đề với nhà nước toàn cầu là sự không nhất quán vốn có mà nó có thể tạo ra. Tài liệu về các bài kiểm tra đơn vị thường giải quyết vấn đề này, vì bất cứ khi nào có trạng thái toàn cầu có thể được truy cập bởi nhiều đối tượng không liên quan, các bài kiểm tra đơn vị của bạn sẽ không đầy đủ và không phải là 'đơn vị'. Như đã đề cập trong bài viết này về trạng thái toàn cầu và singletons , nếu đối tượng A và B không liên quan với nhau (vì trong một không được đưa ra tham chiếu rõ ràng cho người khác), thì A không thể ảnh hưởng đến trạng thái của B.

Có một số trường hợp ngoại lệ đối với trạng thái cấm toàn cầu trong mã tốt, chẳng hạn như đồng hồ. Thời gian là toàn cầu, và - trong một số ý nghĩa - nó thay đổi trạng thái của các đối tượng mà không có mối quan hệ được mã hóa.


"Thời gian là toàn cầu" - có nhiều cách khác để mô hình hóa thời gian trong các hệ thống máy tính hơn là để nó trở thành một thứ toàn cầu tiềm ẩn thay đổi theo cách riêng của nó. xem khảo sát này: "Thời gian mô hình hóa trong tính toán: Phân loại so sánh và khảo sát so sánh" @ arxiv.org/abs/0807.4132
Jared Updike

4

0,02 đô la của tôi là một vài trong số những câu trả lời này gây nhầm lẫn cho vấn đề, thay vì nói "số liệu thống kê là xấu" Tôi nghĩ tốt hơn là nói về phạm vi và trường hợp.

Điều tôi muốn nói là một tĩnh là một biến "lớp" - nó phản hồi lại một giá trị được chia sẻ trên tất cả các phiên bản của lớp đó. Thông thường, nó cũng nên được sắp xếp theo cách đó (được bảo vệ hoặc riêng tư đối với lớp và các thể hiện của nó).

Nếu bạn có kế hoạch đưa hành vi cấp lớp xung quanh nó và đưa nó ra mã khác, thì một singleton có thể là một giải pháp tốt hơn để hỗ trợ các thay đổi trong tương lai (như @Jessica đề xuất). Điều này là do bạn có thể sử dụng các giao diện ở cấp thể hiện / đơn lẻ theo những cách mà bạn không thể sử dụng ở cấp độ lớp - cụ thể là kế thừa.

Một số suy nghĩ về lý do tại sao tôi nghĩ rằng một số khía cạnh trong các câu trả lời khác không phải là cốt lõi của câu hỏi ...

Thống kê không phải là "toàn cầu". Trong phạm vi Java được kiểm soát tách biệt với tĩnh / thể hiện.

Đồng thời không kém phần nguy hiểm cho thống kê so với các phương thức cá thể. Đó vẫn là trạng thái cần được bảo vệ. Chắc chắn rằng bạn có thể có 1000 trường hợp với một biến đối tượng mỗi và chỉ một biến tĩnh, nhưng nếu mã truy cập không được viết theo cách an toàn luồng thì bạn vẫn bị vặn - bạn sẽ mất nhiều thời gian hơn để nhận ra .

Quản lý vòng đời là một lý lẽ thú vị, nhưng tôi nghĩ đó là một vấn đề ít quan trọng hơn. Tôi không thấy lý do tại sao việc quản lý một cặp phương thức lớp như init () / clear () khó hơn so với việc tạo và hủy bỏ một cá thể đơn lẻ. Trong thực tế, một số người có thể nói rằng một singleton phức tạp hơn một chút do GC.

PS, về mặt Smalltalk, nhiều phương ngữ của nó có các biến lớp, nhưng trong các lớp Smalltalk thực sự là các thể hiện của Metaclass nên chúng thực sự là các biến trên ví dụ Metaclass. Tuy nhiên, tôi sẽ áp dụng quy tắc tương tự. Nếu chúng đang được sử dụng cho trạng thái chia sẻ giữa các trường hợp thì ok. Nếu họ đang hỗ trợ chức năng công cộng, bạn nên xem Singleton. Thở dài, tôi chắc chắn bỏ lỡ Smalltalk ....


4

Có hai câu hỏi chính trong bài viết của bạn.

Đầu tiên, về các biến tĩnh. Các biến tĩnh hoàn toàn không cần thiết và có thể tránh sử dụng dễ dàng. Trong OOP languajes nói chung và trong Java nói riêng, các tham số hàm được tham chiếu bởi tham chiếu, điều này có nghĩa là, nếu bạn truyền một đối tượng cho một funciont, bạn đang truyền một con trỏ đến đối tượng, vì vậy bạn không cần xác định các biến tĩnh vì bạn có thể chuyển một con trỏ tới đối tượng cho bất kỳ phạm vi nào cần thông tin này. Ngay cả khi điều này ngụ ý rằng yo sẽ lấp đầy bộ nhớ của bạn bằng các con trỏ, thì điều này sẽ không thể hiện hiệu năng kém vì các hệ thống phân trang bộ nhớ thực tế được tối ưu hóa để xử lý việc này và chúng sẽ duy trì trong bộ nhớ các trang được tham chiếu bởi các con trỏ bạn chuyển đến mới phạm vi; việc sử dụng các biến tĩnh có thể khiến hệ thống tải trang bộ nhớ nơi chúng được lưu trữ khi chúng cần được truy cập (điều này sẽ xảy ra nếu trang không được truy cập trong một thời gian dài). Một thực hành tốt là đặt tất cả các stuf tĩnh đó lại với nhau trong một số "cụm cấu hình" nhỏ, điều này sẽ đảm bảo hệ thống đặt tất cả trong cùng một trang bộ nhớ.

Thứ hai, về phương pháp tĩnh. Các phương thức tĩnh không quá tệ, nhưng chúng có thể nhanh chóng làm giảm hiệu suất. Ví dụ, hãy nghĩ về một phương thức so sánh hai đối tượng của một lớp và trả về một giá trị chỉ ra đối tượng nào lớn hơn (phương thức so sánh tip) phương thức này có thể tĩnh hoặc không, nhưng khi gọi nó, dạng không tĩnh sẽ hiệu quả hơn vì nó sẽ chỉ phải giải quyết hai tham chiếu (một cho mỗi đối tượng) đối với ba tham chiếu sẽ phải giải quyết phiên bản tĩnh của cùng một phương thức (một cho lớp cộng hai, một cho mỗi đối tượng). Nhưng như tôi nói, điều này không tệ lắm, nếu chúng ta nhìn vào lớp Toán, chúng ta có thể tìm thấy rất nhiều hàm toán học được định nghĩa là các phương thức tĩnh. Điều này thực sự hiệu quả hơn việc đưa tất cả các phương thức này vào lớp xác định các số,

Trong concluson: Tránh sử dụng các biến tĩnh và tìm trạng thái cân bằng hiệu suất chính xác khi xử lý các phương thức tĩnh hoặc không tĩnh.

PS: Xin lỗi vì tiếng Anh của tôi.


4

Không có gì sai với các biến tĩnh mỗi se. Đó chỉ là cú pháp Java bị hỏng. Mỗi lớp Java thực sự định nghĩa hai cấu trúc - một đối tượng đơn lẻ đóng gói các biến tĩnh và một cá thể. Xác định cả hai trong cùng một khối nguồn là điều ác thuần túy và dẫn đến một mã khó đọc. Scala đã làm đúng.


3

a) Lý do về các chương trình.

Nếu bạn có một chương trình vừa và nhỏ, nơi truy cập biến tĩnh Global.foo, cuộc gọi đến nó thường đến từ nơi nào - không có đường dẫn, và do đó không có dòng thời gian, làm thế nào biến đó đến nơi, nơi nó biến Được sử dụng. Bây giờ làm thế nào để tôi biết ai đặt nó với giá trị thực của nó? Làm thế nào để tôi biết, điều gì xảy ra, nếu tôi sửa đổi nó ngay bây giờ? Tôi có grep trên toàn bộ nguồn, để thu thập tất cả các truy cập, để biết, những gì đang xảy ra.

Nếu bạn biết cách bạn sử dụng nó, bởi vì bạn vừa viết mã, vấn đề là vô hình, nhưng nếu bạn cố gắng hiểu mã nước ngoài, bạn sẽ hiểu.

b) Bạn có thực sự chỉ cần một?

Các biến tĩnh thường ngăn nhiều chương trình cùng loại chạy trong cùng một JVM với các giá trị khác nhau. Bạn thường không thấy trước việc sử dụng, trong đó có nhiều hơn một phiên bản chương trình của bạn hữu ích, nhưng nếu nó phát triển hoặc nếu nó hữu ích cho người khác, họ có thể gặp tình huống, nơi họ muốn bắt đầu nhiều hơn một phiên bản chương trình của bạn .

Chỉ có nhiều hoặc ít mã vô dụng sẽ không được nhiều người sử dụng trong một thời gian dài theo cách chuyên sâu có thể phù hợp với các biến tĩnh.


3

mọi thứ (có thể :) đều có mục đích của nó, nếu bạn có một loạt các luồng cần chia sẻ / lưu trữ dữ liệu và cả bộ nhớ có thể truy cập (vì vậy bạn không phân chia thành các bối cảnh trong một JVM), tĩnh là lựa chọn tốt nhất

-> tất nhiên bạn có thể ép buộc chỉ là một ví dụ, nhưng tại sao?
tôi tìm thấy một số ý kiến ​​trong chủ đề này ác, không phải là thống kê;)


3

Biến tĩnh không tốt cũng không xấu. Chúng đại diện cho các thuộc tính mô tả toàn bộ lớp và không phải là một trường hợp cụ thể. Nếu bạn cần có một bộ đếm cho tất cả các thể hiện của một lớp nhất định, một biến tĩnh sẽ là nơi thích hợp để giữ giá trị.

Các vấn đề xuất hiện khi bạn cố gắng sử dụng các biến tĩnh để giữ các giá trị liên quan đến thể hiện.


2

Tất cả các câu trả lời trên cho thấy tại sao thống kê là xấu. Lý do họ xấu là vì nó mang lại ấn tượng sai lầm rằng bạn đang viết mã hướng đối tượng, trong khi thực tế bạn không như vậy. Đó chỉ là tà ác.


1
Nhưng việc cứng nhắc xem xét mã của bạn để tuân theo một số mô hình tiêu chuẩn tùy ý có thực sự làm cho mã tốt hơn hay chúng ta đang phàn nàn để tránh chỉ viết mã hoạt động?
Zoey

Vâng, nó làm cho nó tốt hơn, bởi vì nó làm cho nó dễ quản lý hơn trong tương lai, dễ hiểu hơn và rõ ràng hơn.
blockhead

1
Tại sao nó không ác khi không viết mã OO? và tại sao Bjarne Stroustrup không đồng ý với bạn? gọi tên chỉ một ...
Hầu tước Lorne

2
Tôi đã không nói điều ác của nó là không viết mã OO. Tôi đã nói rằng thật tệ khi nghĩ rằng bạn đang viết mã OO, khi tất cả những gì bạn đang ngụy trang trên toàn cầu đằng sau các phương thức và thuộc tính tĩnh. Vui lòng đọc lại những gì tôi đã viết.
blockhead

2

Có rất nhiều câu trả lời hay ở đây, thêm vào đó,

Bộ nhớ: Các biến tĩnh tồn tại miễn là trình nạp lớp tồn tại [nói chung cho đến khi VM chết], nhưng đây chỉ là trong trường hợp các đối tượng / tham chiếu hàng loạt được lưu trữ dưới dạng tĩnh.

Modularization: xem xét các khái niệm như IOC, DepencyInjection, proxy, v.v. Tất cả đều hoàn toàn chống lại việc triển khai khớp nối / tĩnh chặt chẽ.

Khác Con: An toàn chủ đề, Kiểm tra


0

Hãy nghĩ rằng nếu bạn có một ứng dụng có nhiều người dùng và bạn đã xác định một dạng tĩnh, thì mọi người dùng cũng sẽ sửa đổi tất cả các dạng khác của người dùng khác.


0

Tôi nghĩ rằng việc sử dụng quá mức các biến toàn cục với từ khóa tĩnh cũng sẽ dẫn đến rò rỉ bộ nhớ tại một số ví dụ trong applica


0

Theo quan điểm của tôi, staticbiến chỉ nên đọc dữ liệu hoặc biến được tạo bởi quy ước .

Ví dụ: chúng tôi có một số dự án và chúng tôi có một danh sách các quốc gia, ngôn ngữ, vai trò người dùng, v.v. Và chúng tôi có lớp để tổ chức dữ liệu này. chúng tôi hoàn toàn chắc chắn rằng ứng dụng sẽ không hoạt động nếu không có danh sách này. Vì vậy, việc đầu tiên chúng tôi làm trên ứng dụng init là kiểm tra danh sách này để cập nhật và nhận danh sách này từ api (nếu cần). Vì vậy, chúng tôi đồng ý rằng dữ liệu này "luôn luôn" có trong ứng dụng. Nó thực tế chỉ đọc dữ liệu nên chúng tôi không cần quan tâm đến trạng thái của nó - nghĩ về trường hợp này chúng tôi thực sự không muốn có nhiều trường hợp của dữ liệu đó - trường hợp này có vẻ là một ứng cử viên hoàn hảo để tĩnh .


0

Tôi đã chơi với statics rất nhiều và tôi có thể cho bạn một câu trả lời hơi khác không - hoặc có thể là một cách hơi khác để xem xét nó?

Khi tôi đã sử dụng số liệu thống kê trong một lớp (Cả thành viên và phương thức) Cuối cùng tôi cũng bắt đầu nhận thấy rằng lớp của tôi thực sự là hai lớp chia sẻ trách nhiệm - có phần "Tĩnh" hoạt động rất giống một singleton và không có phần nào phần -static (một lớp bình thường). Theo như tôi biết, bạn luôn có thể tách biệt hoàn toàn hai lớp đó bằng cách chỉ chọn tất cả các số liệu thống kê cho một lớp và không thống kê cho lớp kia.

Điều này đã từng xảy ra rất nhiều khi tôi có một bộ sưu tập tĩnh bên trong một lớp giữ các thể hiện của lớp và một số phương thức tĩnh để quản lý bộ sưu tập. Khi bạn nghĩ về nó, rõ ràng là lớp của bạn không làm "Chỉ một điều", đó là một bộ sưu tập và làm một cái gì đó hoàn toàn khác.

Bây giờ, hãy cấu trúc lại vấn đề một chút: Nếu bạn chia lớp của bạn thành một lớp trong đó mọi thứ đều tĩnh và một lớp khác chỉ là "Lớp bình thường" và quên đi "Lớp bình thường" thì câu hỏi của bạn sẽ hoàn toàn là lớp Tĩnh so với Singleton. được giải quyết theo chiều dài ở đây (và có lẽ là một tá câu hỏi khác).

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.