Tôi có nên sử dụng lại các biến?


59

Tôi có nên sử dụng lại các biến?

Tôi biết rằng nhiều thực tiễn tốt nhất nói rằng bạn không nên làm điều đó, tuy nhiên, sau đó, khi nhà phát triển khác nhau đang gỡ lỗi mã và có 3 biến giống nhau và sự khác biệt duy nhất là chúng được tạo ở những nơi khác nhau trong mã, anh ta có thể bối rối. Kiểm thử đơn vị là một ví dụ tuyệt vời về điều này.

Tuy nhiên, tôi làm biết rằng thực hành tốt nhất là phần lớn thời gian chống lại nó. Ví dụ, họ nói không "ghi đè" tham số phương thức.

Các thực tiễn tốt nhất thậm chí còn chống lại việc vô hiệu hóa các biến trước đó (trong Java có Sonar đưa ra cảnh báo khi bạn gán nullcho biến, rằng bạn không cần phải làm điều đó để gọi trình thu gom rác kể từ Java 6. Bạn không thể luôn kiểm soát cảnh báo nào bị tắt; hầu hết thời gian mặc định được bật.)


11
Kể từ Java 6? Bạn không bao giờ cần gán null cho các biến trong bất kỳ phiên bản Java nào. Khi một biến đi ra khỏi phạm vi, nó sẽ giải phóng tham chiếu đến đối tượng của nó.
Kevin Panko

35
sử dụng lại các biến là một trong những điều đầu tiên mà các trình mã hóa mã sử dụng
Lelouch Lamperouge

7
Biến sử dụng lại xung đột với việc chọn định danh hợp lý cho các biến của bạn. Trong các ngôn ngữ hiện đại, thực sự không có bất kỳ lợi thế nào cho việc sử dụng lại biến đổi vượt trội hơn lợi ích của việc có các định danh tốt.
Joren

4
Cũng lưu ý rằng không phải tất cả tái sử dụng là tái sử dụng. Các lập trình viên FORTRAN cũ, ngay cả những người đã chuyển sang các ngôn ngữ khác, thường xuyên sử dụng I, J, K, L, M, N (hoặc i, j, k, l, m, n) cho tất cả các vòng lặp DO của chúng tôi (for- vòng lặp) quầy. Chúng ta thường thấy những thứ như SUM = SUM + A (I, K) * B (K, J) hoặc sum + = a [i] [k] * b [k] [j];.
John R. Strohm

1
Việc sử dụng lại các biến có thể được biện minh khi lập trình vi điều khiển với nguồn lực rất hạn chế (một vài kBytes RAM).
m.Alin

Câu trả lời:


130

Vấn đề của bạn chỉ xuất hiện khi các phương thức của bạn dài và đang thực hiện nhiều tác vụ theo một trình tự. Điều này làm cho mã khó hiểu hơn (và do đó duy trì) mỗi se. Việc sử dụng lại các biến thêm vào đầu yếu tố này là một yếu tố rủi ro bổ sung, làm cho mã trở nên khó theo dõi hơn và dễ bị lỗi hơn.

Thực hành tốt nhất của IMO là sử dụng các phương pháp đủ ngắn, chỉ làm một việc, loại bỏ toàn bộ vấn đề.


Còn kiểm tra đơn vị thì sao? ví dụ tôi cần kiểm tra nếu sau khi thực hiện phương thức lần thứ hai thì nó vẫn hoạt động như mong đợi.
IAd CHƯƠNG

20
@IAd CHƯƠNG, mã kiểm tra đơn vị là một vấn đề khác, trong đó các tiêu chuẩn có thể thoải mái hơn so với mã sản xuất. Tuy nhiên, làm cho nó dễ đọc và dễ bảo trì là ưu tiên cao. Bạn cũng có thể (và nên) trích xuất các phương pháp từ các bài kiểm tra đơn vị của mình, để giữ cho chúng ngắn gọn và dễ đọc (và để loại bỏ nhu cầu sử dụng lại các biến).
Péter Török

3
@IAd CHƯƠNG, đối với kịch bản bạn mô tả, tôi sẽ không sử dụng lại một biến. Điều thích hợp sẽ là một cái gì đó giống như result1 = foo(); result2 = foo(); Assert.AreEqual(result1, result2);tôi không thấy nhiều nơi mà bạn cần sử dụng lại một biến trong trường hợp này.
JSB

1
@IAd CHƯƠNG for (int i = 0; i <2; i ++) {result = foo (); assertEquals (kỳ vọng, kết quả);}
Bill K

2
+1 - đây là một mùi mã tốt khi thực hiện các phương thức nhỏ hơn.

49

Tái sử dụng biến trong một phương thức là một dấu hiệu mạnh mẽ cho thấy bạn nên cấu trúc lại / tách nó.

Vì vậy, câu trả lời của tôi là bạn không nên sử dụng lại chúng, bởi vì nếu bạn làm thế thì việc tái cấu trúc nó sẽ khó hơn nhiều.


19

Nó phụ thuộc.

Một số biến có thể được tạo chính xác cho mục đích giữ một loại dữ liệu nhất định, có thể thay đổi trong quá trình thực thi hàm. Mã trả về đến với tâm trí ở đây, ví dụ:

void my_function() {
    HRESULT errorcode;
    errorcode = ::SomeWindowsApiFunction();
    if (FAILED(errorcode)) { /* handle error */ }
    errorcode = ::AnotherWindowsApiFunction();
    if (FAILED(errorcode)) { /* handle error */ }
}

Tên biến làm cho nó rất rõ ràng những gì biến này được dự định để lưu trữ. Tôi nghĩ rằng các usecase khác như điều này là có thể, trong đó một biến về mặt khái niệm là một thùng chứa được sử dụng hợp lý bởi các trường hợp khác nhau của những thứ rất giống nhau trong quá trình của một hàm.

Tuy nhiên, nói chung, điều này chỉ nên được thực hiện trong những trường hợp hoàn toàn rõ ràng đối với những người đọc mã có thể. Trong mọi trường hợp, ngoại trừ có thể tối ưu hóa cực kỳ không liên quan đến mức độ dễ đọc của mã, nên sử dụng một biến chỉ vì loại phù hợp.

Tất cả điều này về cơ bản xuất phát từ thực tiễn tốt trong việc đặt tên biến. Tên nên nói cho chính họ. Nếu khó có thể đặt mục đích chính xác cho tất cả các lần sử dụng lại trong một tên ngắn, tốt nhất là chỉ sử dụng các biến khác biệt.


15

Lý do lớn nhất tôi không sử dụng lại các biến (đặc biệt là trong các thử nghiệm đơn vị) là vì nó đưa ra một đường dẫn mã không cần thiết, một đường dẫn khó kiểm tra và gỡ lỗi. Một bài kiểm tra đơn vị tốt phải độc lập với các bài kiểm tra khác và khi bạn sử dụng lại biến cấp độ lớp (ví dụ) trong một bài kiểm tra đơn vị, bạn phải đảm bảo khẳng định trạng thái của chúng trước mỗi bài kiểm tra. Một thử nghiệm đơn vị tốt cũng cô lập các khuyết tật, vì vậy về mặt lý thuyết, mỗi trường hợp thử nghiệm (phương pháp) chỉ nên xác nhận 1 hành vi cho hệ thống được thử nghiệm. Nếu các phương thức kiểm tra của bạn được viết như thế này, hiếm khi có nhu cầu hoặc lợi ích để sử dụng lại một biến mức phương thức. Cuối cùng, trong các ngôn ngữ hỗ trợ các bao đóng và xử lý không đồng bộ, thực sự rất khó để suy luận về những gì đang xảy ra nếu bạn đang sử dụng lại các biến trong suốt một phương thức.


vì vậy tôi nên viết các phương thức thử nghiệm như -testS Something và khẳng định cho việc gọi phương thức đơn -testS SomethingTwoInvocations và khẳng định cho lần gọi thứ hai?
IAd CHƯƠNG

2
Đúng. Nó giúp bạn xác định chính xác nếu đó là lần lặp đầu tiên hoặc lần thứ hai thất bại. mspec có thể giúp cho việc này, cây thừa kế của nó sẽ rất hữu ích.
Bryan Boettcher

Nếu bạn sử dụng "biến" khi bạn có nghĩa là "biến lớp" hoặc "biến thể hiện" thì bạn cần thể hiện bản thân rõ ràng hơn.
gnasher729

13

Bạn nên sử dụng các biến khác nhau. Nếu bạn lo lắng rằng đồng nghiệp của bạn sẽ bị nhầm lẫn, hãy đặt cho họ những cái tên thể hiện rõ vai trò của họ.
Sử dụng lại các biến là một nguồn có thể gây nhầm lẫn trong tương lai; tốt hơn để xóa nó ngay bây giờ.
Trong một số trường hợp, tên biến tương tự có thể được sử dụng lại; ví dụ itrong một vòng lặp đếm đơn giản. Trong những trường hợp này, tất nhiên bạn nên đảm bảo rằng các biến nằm trong phạm vi riêng của chúng.

EDIT: Việc sử dụng lại các biến đôi khi là một dấu hiệu cho thấy Nguyên tắc Trách nhiệm Đơn lẻ bị vi phạm. Kiểm tra xem nếu biến được sử dụng lại được sử dụng trong cùng một vai trò. Nếu có, nó hoàn toàn không thể sử dụng lại (mặc dù vẫn có thể có hai biến khác nhau, để hạn chế phạm vi của các biến đã nói). Nếu nó được sử dụng trong các vai trò khác nhau, bạn có vi phạm SRP trên tay.


4

Có một tình huống trong đó bạn có thể muốn sử dụng lại một biến bất kể lời khuyên tuyệt vời được đưa ra bởi các câu trả lời khác: khi trình biên dịch của bạn cần một bàn tay trợ giúp.

Trong một số trường hợp, trình biên dịch của bạn có thể không đủ thông minh để nhận ra rằng một biến được cấp phát đăng ký nhất định không còn được sử dụng bởi phần tiếp theo của mã. Do đó, nó sẽ không sử dụng lại đăng ký miễn phí về mặt lý thuyết cho các biến tiếp theo và mã được tạo có thể là tối ưu phụ.

Lưu ý rằng tôi không biết bất kỳ trình biên dịch chính hiện tại nào không nắm bắt được tình huống này một cách chính xác, vì vậy, sẽ không bao giờ, làm điều này trừ khi bạn biết rằng trình biên dịch của bạn đang tạo mã tối ưu phụ. Nếu bạn đang biên dịch cho các hệ thống nhúng đặc biệt với trình biên dịch tùy chỉnh, bạn vẫn có thể gặp phải sự cố này.


17
-1 trừ khi bạn có thể chỉ ra một tình huống trong thế giới thực nơi điều này thực sự xảy ra và việc sử dụng lại một biến tạo ra sự khác biệt đáng kể. Đây là một giải pháp lý thuyết cho một vấn đề lý thuyết, và không phải là một giải pháp rất tốt ở đó. Trường hợp trình biên dịch quyết định đặt các biến là mối quan tâm của trình biên dịch; nếu trình biên dịch của bạn tạo mã kém và nếu nó thực sự tạo ra sự khác biệt, thì hãy sửa hoặc thay thế trình biên dịch.
Caleb

3
@Caleb - bạn không phải lúc nào cũng có quyền truy cập vào mã nguồn trình biên dịch cho nền tảng bạn đang phát triển, đặc biệt là đối với các nền tảng nhúng độc quyền. Tôi cũng DID nói trong câu trả lời của mình rằng tôi không biết bất kỳ trình biên dịch chính hiện hành nào (hoặc thông dịch viên / VM thực tế) không thực hiện chính xác phân tích sinh động này trong tất cả các trường hợp mà tôi đã xem xét. Tuy nhiên, tôi đã làm việc trên các hệ thống nhúng độc quyền với các trình biên dịch tùy chỉnh rất đơn giản trong quá khứ (10 năm trước hoặc lâu hơn). Hệ thống rất hạn chế về khả năng và trình biên dịch cũng vậy, vì vậy tình huống này đã xảy ra ở đó.
Joris Timmermans

2
+1 Tôi đã thực hiện chính xác việc tái sử dụng như vậy trong dự án trước đây (mã phương tiện được tối ưu hóa cao được viết bằng ANSI C). Theo như tôi nhớ thì sự khác biệt có thể dễ dàng nhìn thấy trong lắp ráp và có thể đo lường được trong các điểm chuẩn. Chưa bao giờ làm và hy vọng rằng sẽ không bao giờ phải sử dụng lại như vậy trong Java.
gnat

@Caleb sửa chữa hoặc thay thế trình biên dịch có thể không phải là một tùy chọn vì một số lý do và bạn chỉ cần thực hiện với những gì bạn có.

@ ThorbjørnRavnAndersen Có thể hiểu được rằng người ta có thể bị buộc phải sử dụng trình biên dịch tệ hại, và hơn nữa hiệu năng có thể rất quan trọng đến mức bạn cần phải đoán trình biên dịch thứ hai và thao tác mã của bạn để sử dụng lại đăng ký? Đúng. Có khả năng không? Không. MadKeithV đồng ý rằng anh ta không thể đưa ra một ví dụ thực tế. Đó có phải là một thực hành tốt nhất ? Đó có phải là phong cách tốt ? Nó có phải là một chỉ số về chất lượng mã ? Chắc chắn không phải. Đây có phải là một câu trả lời hữu ích cho câu hỏi như được viết? Do tôn trọng MadKeithV, nhưng tôi không nghĩ vậy.
Caleb

2

Tôi sẽ nói không.

Hãy suy nghĩ về kịch bản này: chương trình của bạn bị sập và bạn cần tìm ra điều gì đã xảy ra bằng cách kiểm tra một bãi chứa cốt lõi ... bạn có thể thấy sự khác biệt không? ;-)


Nếu kết xuất lõi của bạn là từ một bản dựng được tối ưu hóa, nó có thể có các biến chia sẻ bộ nhớ. Vì vậy, nó ít hữu ích hơn thì nó có thể.
Winston Ewert

Tất nhiên, một bản dựng được tối ưu hóa không hữu ích cho việc gỡ lỗi! nhưng trong mọi trường hợp bạn vẫn có tùy chọn sử dụng bản dựng gỡ lỗi ... Và khi bạn có trình gỡ lỗi được đính kèm, bạn không cần phải đi từng bước từ đầu hàm để xem các biến thay đổi như thế nào, bởi vì chúng đừng! :-D
fortran

2

Không, bạn không cải thiện mã mà máy đang chạy (... mã lắp ráp). Để lại việc sử dụng bất cứ bộ nhớ nào mà trình biên dịch sử dụng cho biến cho trình biên dịch. Thông thường, nó sẽ là một đăng ký, và sử dụng lại không mua gì cho bạn. Tập trung vào việc làm cho mã dễ đọc hơn.


0

Nó phụ thuộc. Trong một ngôn ngữ động, trong đó kiểu nằm trên các giá trị thay vì các biến thì nếu tôi có một đối số được đặt tên tốt cho một hàm, ví dụ, là một chuỗi. Tôi thay đổi thuật toán có nghĩa là nó luôn được sử dụng sau khi được hiểu là số nguyên, sau đó, như một bản nháp đầu tiên tôi có thể làm tương đương với

Scrote = int (Scrote)

Nhưng cuối cùng tôi sẽ tìm cách thay đổi loại được gửi đến hàm và lưu ý sự thay đổi trong loại tham số.


1
Đặt một dòng như "Scrote = int (Scrote)" ở giữa một chuỗi mã dài là một cách tuyệt vời để khiến các lập trình viên bảo trì phát điên.
jhocking

Vấn đề có nhiều khả năng là với "lô mã dài" mà bạn đề cập.
Paddy3118

Cũng như các ghi chú câu trả lời được chấp nhận, việc sử dụng lại các biến chỉ thực sự là một vấn đề với các lô mã dài. Về cơ bản, trong bất kỳ tình huống nào bạn sẽ viết một cái gì đó như "Scrote = int (Scrote)" tôi muốn đặt int (Scrote) vào một biến mới hoặc tốt hơn là chuyển int (Scrote) sang một hàm mới.
jhocking

0

Đầu tiên, hãy nhìn vào môi trường phát triển của bạn. Nếu bạn gặp vấn đề gỡ lỗi vì bạn có năm biến trong các phạm vi khác nhau có tên giống nhau và trình gỡ lỗi không hiển thị cho bạn biến nào trong số các biến này là biến bạn cần, thì đừng sử dụng năm biến có cùng tên. Có hai cách để đạt được điều này: Sử dụng một biến hoặc sử dụng năm tên khác nhau.

Trình gỡ lỗi của bạn cũng có thể gây khó khăn khi gỡ lỗi một hàm có nhiều biến. Nếu một hàm có 20 biến khó gỡ lỗi hơn một biến có 16 biến, thì bạn có thể xem xét thay thế 5 biến bằng một biến. ("Có thể cân nhắc" không giống như "nên luôn luôn").

Sẽ có một biến được sử dụng ở nhiều nơi miễn là biến đó luôn có cùng mục đích. Ví dụ: nếu mười lệnh gọi hàm trả về mã lỗi, được xử lý ngay lập tức cho mỗi cuộc gọi, hãy sử dụng một biến chứ không phải 10. Nhưng không sử dụng cùng một biến cho những thứ hoàn toàn khác nhau. Giống như sử dụng "tên" cho tên khách hàng và 10 dòng sau sử dụng cùng một biến cho tên công ty, điều đó thật tệ và sẽ khiến bạn gặp rắc rối. Tồi tệ hơn, sử dụng "tên khách hàng" cho tên khách hàng và 10 dòng sau sử dụng cùng một biến "tên khách hàng" cho tên công ty.

Điều quan trọng, không có gì là một quy tắc sắt. Cái gì cũng có ưu điểm và nhược điểm. Nếu "thực tiễn tốt nhất" gợi ý một điều, và bạn có lý do để nói rằng đó là một ý tưởng tồi trong tình huống cụ thể của bạn, thì đừng làm điều đó.


0

Đầu tiên, hãy nhìn vào môi trường phát triển của bạn. Nếu bạn gặp vấn đề gỡ lỗi vì bạn có năm biến trong các phạm vi khác nhau có tên giống nhau và trình gỡ lỗi không hiển thị cho bạn biến nào trong số các biến này là biến bạn cần, thì đừng sử dụng năm biến có cùng tên. Có hai cách để đạt được điều này: Sử dụng một biến hoặc sử dụng năm tên khác nhau.

Trình gỡ lỗi của bạn cũng có thể gây khó khăn khi gỡ lỗi một hàm có nhiều biến. Nếu một hàm có 20 biến khó gỡ lỗi hơn một biến có 16 biến, thì bạn có thể xem xét thay thế 5 biến bằng một biến. ("Có thể cân nhắc" không giống như "nên luôn luôn").

Sẽ có một biến được sử dụng ở nhiều nơi miễn là biến đó luôn có cùng mục đích. Ví dụ: nếu mười lệnh gọi hàm trả về mã lỗi, được xử lý ngay lập tức cho mỗi cuộc gọi, hãy sử dụng một biến và không 10. Có một vấn đề với điều này: Thông thường, trình biên dịch sẽ cho bạn biết khi bạn sử dụng biến chưa được khởi tạo. Nhưng ở đây, nếu bạn đọc mã lỗi sau cuộc gọi 6, nhưng biến không thực sự được thay đổi sau cuộc gọi 5, bạn sẽ nhận được dữ liệu vô dụng mà không cần trình biên dịch cảnh báo cho bạn. Bởi vì biến đã được khởi tạo, với dữ liệu hiện vô dụng.

Điều quan trọng, không có gì là một quy tắc sắt. Cái gì cũng có ưu điểm và nhược điểm. Nếu "thực tiễn tốt nhất" gợi ý một điều, và bạn có lý do để nói rằng đó là một ý tưởng tồi trong tình huống cụ thể của bạn, thì đừng làm điều đó.


-2

Sử dụng các biến toàn cục khi bạn cần duy trì trạng thái hoặc lưu trữ hằng. Bằng cách sử dụng lại các biến, bạn chỉ sử dụng lại tên trỏ đến một vị trí trong bộ nhớ. Tên mô tả về khởi tạo chỉ có thể giúp bạn hiểu rõ hơn (Giả sử Java và không có OCD nguyên thủy).

Trừ khi bạn vào mã obfuscation bằng tay hoặc gây khó chịu cho các nhà phát triển 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.