Chúng ta có nên loại bỏ các biến cục bộ nếu chúng ta có thể?


95

Ví dụ: để giữ CPU trên Android, tôi có thể sử dụng mã như thế này:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc");
wakeLock.acquire();

nhưng tôi nghĩ các biến cục bộ powerManagerwakeLockcó thể được loại bỏ:

((PowerManager)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag")
    .acquire();

cảnh tương tự xuất hiện trong chế độ xem cảnh báo của iOS, ví dụ: từ

UIAlertView *alert = [[UIAlertView alloc]
    initWithTitle:@"my title"
    message:@"my message"
    delegate:nil
    cancelButtonTitle:@"ok"
    otherButtonTitles:nil];
[alert show];

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [alertView release];
}

đến:

[[[UIAlertView alloc]
    initWithTitle:@"my title"
    message:@"my message"
    delegate:nil
    cancelButtonTitle:@"ok"
    otherButtonTitles:nil] show];

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [alertView release];
}

Đó có phải là một thực tiễn tốt để loại bỏ một biến cục bộ nếu nó chỉ được sử dụng một lần trong phạm vi?


95
Không cần thiết. Đôi khi nó làm cho mã rõ ràng hơn để sử dụng các biến một lần và trong hầu hết các ngôn ngữ lập trình đàng hoàng, có rất ít (nếu có) chi phí thời gian chạy cho các biến bổ sung đó.
Robert Harvey

75
Nó cũng làm cho việc bước qua mã w / trình gỡ lỗi trở nên khó khăn hơn. Và bạn cũng phải chắc chắn (tùy thuộc vào ngôn ngữ) biểu thức đầu tiên không phải là NULL hoặc lỗi.
GrandmasterB

78
Không, không phải vậy. Trong thực tế, đó là một thực tiễn tốt để giới thiệu các biến cục bộ để phá vỡ chuỗi các cuộc gọi phương thức. Mã máy được tạo có khả năng giống hệt nhau và mã nguồn gần như được đảm bảo dễ đọc hơn, do đó tốt hơn.
Kilian Foth

48
Hãy thử điều đó. Bây giờ hãy cắm trình gỡ lỗi và thử xem trạng thái của PowerManager hoặc WakeLock. Nhận ra sai lầm của bạn. Tôi đã từng nghĩ giống nhau ("những gì với tất cả những người địa phương này?") Cho đến khi tôi phải dành phần lớn thời gian để điều tra mã.
Faerindel

42
Ngay cả trong ví dụ của bạn, phiên bản "bị loại" của bạn có thanh cuộn và hoàn toàn không vừa với màn hình của tôi, điều này khiến nó khó đọc hơn rất nhiều.
Erik

Câu trả lời:


235

Mã được đọc thường xuyên hơn nhiều so với mã được viết, vì vậy bạn nên thương hại cho linh hồn tội nghiệp, người sẽ phải đọc mã sáu tháng kể từ bây giờ (có thể là bạn) và cố gắng tìm mã rõ ràng nhất, dễ hiểu nhất. Theo tôi, hình thức đầu tiên, với các biến cục bộ, dễ hiểu hơn nhiều. Tôi thấy ba hành động trên ba dòng, thay vì ba hành động trên một dòng.

Và nếu bạn nghĩ rằng bạn đang tối ưu hóa bất cứ điều gì bằng cách loại bỏ các biến cục bộ, thì bạn không làm vậy. Một trình biên dịch hiện đại sẽ đưa powerManagervào một thanh ghi 1 , cho dù một biến cục bộ có được sử dụng hay không, để gọi newWakeLockphương thức. Điều này cũng đúng với wakeLock. Vì vậy, bạn kết thúc với cùng một mã được biên dịch trong cả hai trường hợp.

1 Nếu bạn có nhiều mã can thiệp giữa khai báo và sử dụng biến cục bộ, nó có thể đi vào ngăn xếp, nhưng đó là một chi tiết nhỏ.


2
Bạn không thể biết liệu có nhiều mã hay không, trình tối ưu hóa có thể thực hiện một số chức năng ... Mặt khác, đồng ý, phấn đấu để dễ đọc trước tiên là bước đi đúng đắn.
Matthieu M.

2
Chà, tôi nghĩ ngược lại, nếu tôi thấy một biến cục bộ được khởi tạo bội số lần trên mỗi hàm trong đó nó được sử dụng thay vì là một thuộc tính khi có thể, tôi sẽ tìm kiếm tại sao, về cơ bản tôi sẽ đọc kỹ các dòng và so sánh chúng với hãy chắc chắn rằng chúng giống nhau Vì vậy tôi sẽ lãng phí thời gian.
Walfrat

15
@Walfrat Các biến cục bộ được khởi tạo nhiều lần? Ôi. Không ai đề nghị tái sử dụng người dân địa phương :)
Luaan

32
@Walfrat thành viên gây ra tác dụng phụ và chỉ nên được sử dụng khi bạn đặc biệt muốn duy trì trạng thái giữa các cuộc gọi đến các thành viên công cộng. Câu hỏi này dường như để đối phó với việc sử dụng cục bộ để lưu trữ tạm thời các tính toán trung gian.
Gusdor

4
Q: Chúng ta có nên loại bỏ các biến cục bộ nếu có thể? A: Không
simon

94

Một số ý kiến ​​đánh giá cao đã nêu điều này, nhưng không có câu trả lời nào tôi thấy, vì vậy tôi sẽ thêm nó dưới dạng câu trả lời. Yếu tố chính của bạn trong việc quyết định vấn đề này là:

Gỡ lỗi

Thông thường, các nhà phát triển dành nhiều thời gian và nỗ lực gỡ lỗi hơn nhiều so với viết mã.

Với các biến cục bộ, bạn có thể:

  • Điểm dừng trên đường được chỉ định (với tất cả các trang trí điểm dừng khác, chẳng hạn như điểm dừng có điều kiện, v.v ...)

  • Kiểm tra / xem / in / thay đổi giá trị của biến cục bộ

  • Bắt các vấn đề phát sinh do phôi.

  • Có dấu vết ngăn xếp rõ ràng (dòng XYZ có một thao tác thay vì 10)

Không có biến cục bộ, tất cả các tác vụ trên khó hơn, cực kỳ khó hoặc hoàn toàn không thể, tùy thuộc vào trình gỡ lỗi của bạn.

Do đó, hãy làm theo câu châm ngôn khét tiếng (viết mã theo cách mà bạn sẽ làm nếu nhà phát triển tiếp theo duy trì nó sau khi bạn là một kẻ mất trí điên cuồng biết bạn sống ở đâu) và lỗi về khả năng gỡ lỗi dễ dàng hơn , nghĩa là sử dụng các biến cục bộ .


20
Tôi sẽ thêm rằng stacktraces ít hữu ích hơn khi có nhiều cuộc gọi trên một dòng. Nếu bạn có một ngoại lệ con trỏ null trên dòng 50 và có 10 cuộc gọi trên dòng đó thì điều đó không thu hẹp nhiều thứ. Trong một ứng dụng sản xuất, đây thường sẽ là phần lớn thông tin bạn nhận được từ một báo cáo lỗi.
JimmyJames

3
Bước qua mã cũng khó hơn nhiều. Nếu có cuộc gọi () -> cuộc gọi () -> cuộc gọi () -> cuộc gọi (), thật khó để bước vào cuộc gọi phương thức thứ ba. Dễ dàng hơn nhiều nếu có bốn biến cục bộ
gnasher729

3
@FrankPuffer - chúng ta phải đối phó với thế giới thực. Trường hợp trình gỡ lỗi không làm những gì bạn đề xuất.
DVK

2
@DVK: Trên thực tế có nhiều trình sửa lỗi hơn tôi nghĩ rằng cho phép bạn kiểm tra hoặc xem bất kỳ biểu hiện nào. MS Visual Studio (kể từ phiên bản 2013) có tính năng này cho C ++ và C #. Eclipse có nó cho Java. Trong một bình luận khác, Faerindel đã đề cập đến IE11 cho JS.
Frank Puffer

4
@DVK: Vâng, nhiều người sửa lỗi trong thế giới thực không làm những gì tôi đề xuất. Nhưng điều đó không liên quan gì đến phần đầu tiên trong nhận xét của tôi. Tôi chỉ thấy thật điên rồ khi cho rằng người sẽ duy trì mã của tôi sẽ chủ yếu tương tác với nó bằng một trình gỡ lỗi. Trình gỡ lỗi rất tốt cho các mục đích nhất định nhưng nếu họ có được công cụ chính để phân tích mã, tôi sẽ nói rằng có điều gì đó sai nghiêm trọng và tôi sẽ cố gắng sửa nó thay vì làm cho mã dễ bị gỡ lỗi hơn.
Frank Puffer

47

Chỉ khi nó làm cho mã dễ hiểu hơn. Trong ví dụ của bạn, tôi nghĩ nó làm cho nó khó đọc hơn.

Loại bỏ các biến trong mã được biên dịch là một hoạt động tầm thường đối với bất kỳ trình biên dịch đáng kính nào. Bạn có thể tự kiểm tra đầu ra để xác minh.


1
Điều đó có liên quan nhiều đến kiểu dáng như việc sử dụng các biến. Câu hỏi đã được chỉnh sửa.
Jodrell

29

Câu hỏi của bạn "có phải là một thực tiễn tốt để loại bỏ một biến cục bộ nếu nó chỉ được sử dụng một lần trong phạm vi?" đang kiểm tra các tiêu chí sai. Tiện ích của biến cục bộ không phụ thuộc vào số lần sử dụng nhưng liệu nó có làm cho mã rõ ràng hơn không. Dán nhãn các giá trị trung gian bằng các tên có ý nghĩa có thể cải thiện sự rõ ràng trong các tình huống như cái bạn trình bày vì nó chia mã thành các phần nhỏ hơn, dễ tiêu hóa hơn.

Theo quan điểm của tôi, mã chưa sửa đổi mà bạn trình bày rõ ràng hơn mã sửa đổi của bạn và do đó nên được giữ nguyên. Trên thực tế, tôi cân nhắc việc rút một biến cục bộ ra khỏi mã sửa đổi của bạn để cải thiện sự rõ ràng.

Tôi sẽ không mong đợi bất kỳ tác động hiệu suất nào từ các biến cục bộ và ngay cả khi có một biến số có thể quá nhỏ để có thể xem xét trừ khi mã nằm trong một vòng lặp rất chặt chẽ trong phần quan trọng của chương trình.


Tôi nghĩ rằng điều đó có liên quan nhiều đến kiểu dáng và việc sử dụng khoảng trắng như bất cứ điều gì. Câu hỏi đã được chỉnh sửa.
Jodrell

15

Có lẽ.

Cá nhân tôi sẽ ngần ngại loại bỏ các biến cục bộ nếu có một typecast. Tôi thấy phiên bản nhỏ gọn của bạn ít đọc hơn vì số lượng dấu ngoặc bắt đầu đạt đến giới hạn tinh thần mà tôi có. Nó thậm chí còn giới thiệu một bộ dấu ngoặc mới không cần thiết trong phiên bản dài dòng hơn một chút bằng cách sử dụng một biến cục bộ.

Làm nổi bật cú pháp được cung cấp bởi trình soạn thảo mã có thể giảm thiểu những lo ngại đó đến một mức độ nào đó.

Trước mắt tôi, đây là sự thỏa hiệp tốt hơn:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc").acquire();

Do đó, giữ một biến cục bộ và bỏ qua một biến khác. Trong C # tôi sẽ sử dụng từ khóa var trên dòng đầu tiên vì typecast đã cung cấp thông tin loại.


13

Mặc dù tôi chấp nhận tính hợp lệ của các câu trả lời ủng hộ các biến cục bộ, tôi sẽ đóng vai người ủng hộ của quỷ và đưa ra một quan điểm trái ngược.

Cá nhân tôi phần nào chống lại việc sử dụng các biến cục bộ hoàn toàn cho mục đích tài liệu, mặc dù lý do đó chỉ ra rằng thực tiễn có giá trị: các biến cục bộ giả tài liệu mã một cách hiệu quả.

Trong trường hợp của bạn, vấn đề chính là sự thụt lề chứ không phải sự vắng mặt của các biến. Bạn có thể (và nên) định dạng mã như sau:

((PowerManager)getSystemService(POWER_SERVICE))
        .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag")
        .acquire();

So sánh với phiên bản với các biến cục bộ:

PowerManager powerManager=(PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"abc");
wakeLock.acquire();

Với các biến cục bộ: các tên thêm một số thông tin cho người đọc và các loại được chỉ định rõ ràng, nhưng các cuộc gọi phương thức thực tế ít nhìn thấy hơn và (theo tôi) nó không đọc rõ ràng.

Không sử dụng các biến cục bộ: nó ngắn gọn hơn và các lệnh gọi phương thức hiển thị rõ hơn, nhưng bạn có thể yêu cầu thêm thông tin về những gì đang được trả về. Một số IDE có thể cho bạn thấy điều này tuy nhiên.

Ba ý kiến ​​thêm:

  1. Theo tôi, việc thêm mã không có chức năng sử dụng đôi khi có thể gây nhầm lẫn vì người đọc phải nhận ra rằng nó thực sự không có chức năng sử dụng và chỉ đơn giản là có "tài liệu". Ví dụ: nếu phương thức này dài 100 dòng thì sẽ không rõ ràng rằng các biến cục bộ (và do đó kết quả của lệnh gọi phương thức) không được yêu cầu tại một số điểm sau đó trừ khi bạn đọc toàn bộ phương thức.

  2. Thêm các biến cục bộ có nghĩa là bạn phải chỉ định các loại của chúng và điều này đưa ra một phụ thuộc trong mã mà nếu không sẽ không có ở đó. Nếu kiểu trả về của các phương thức thay đổi không đáng kể (ví dụ, nó đã được đổi tên), bạn sẽ phải cập nhật mã của mình, trong khi không có các biến cục bộ thì bạn sẽ không.

  3. Việc gỡ lỗi có thể dễ dàng hơn với các biến cục bộ nếu trình gỡ lỗi của bạn không hiển thị các giá trị được trả về từ các phương thức. Nhưng cách khắc phục cho điều đó là sửa lỗi thiếu sót trong trình gỡ lỗi, không thay đổi mã.


Về ý kiến ​​thêm của bạn: 1. Đây không phải là vấn đề nếu bạn tuân theo quy ước rằng các biến cục bộ được khai báo càng sát càng tốt với nơi chúng được sử dụng và bạn giữ cho phương thức của mình có độ dài hợp lý. 2. Không phải trong một ngôn ngữ lập trình với suy luận kiểu. 3. Mã được viết tốt thường không cần trình sửa lỗi.
Robert Harvey

2
Ví dụ mã đầu tiên của bạn chỉ hoạt động nếu bạn tạo các đối tượng của mình bằng các giao diện trôi chảy hoặc các mẫu "trình tạo", mà tôi sẽ không sử dụng ở mọi nơi trong mã của mình, mà chỉ chọn lọc.
Robert Harvey

1
Tôi nghĩ rằng câu hỏi giả định rằng các biến cục bộ là chức năng dự phòng và do đó loại giao diện đó là bắt buộc cho trường hợp. Chúng tôi có lẽ có thể đạt được việc loại bỏ người dân địa phương nếu nó khác thông qua các mâu thuẫn khác nhau, nhưng tôi nghĩ rằng chúng tôi đang xem xét trường hợp đơn giản.
rghome

1
Tôi thấy biến thể của bạn thậm chí còn tồi tệ hơn cho khả năng đọc. Tôi có thể đã tiếp xúc với VB.net quá nhiều, nhưng điều đầu tiên của tôi khi nhìn thấy mẫu của bạn là "Câu lệnh VỚI đã đi đâu? Tôi đã vô tình xóa nó?" Tôi cần xem xét gấp đôi những gì đang diễn ra và điều đó không tốt khi tôi cần xem lại mã nhiều tháng sau đó.
Tonny

1
@Tonny đối với tôi, nó dễ đọc hơn: người dân địa phương không thêm bất cứ điều gì không rõ ràng từ bối cảnh. Tôi có đầu Java của tôi, vì vậy không có withtuyên bố. Nó sẽ là các quy ước định dạng bình thường trong Java.
rghome

6

Có một sự căng thẳng của động lực ở đây. Việc giới thiệu các biến tạm thời có thể làm cho mã dễ đọc hơn. Nhưng họ cũng có thể ngăn chặn và làm cho khó nhìn hơn, các phép tái cấu trúc có thể khác, như Phương pháp trích xuấtThay thế Temp bằng Truy vấn . Các kiểu tái cấu trúc sau này, khi thích hợp, thường mang lại lợi ích lớn hơn nhiều so với biến tạm thời.

Về động lực cho những lần tái cấu trúc sau này, Fowler viết :

"Vấn đề với temps là chúng tạm thời và cục bộ. Bởi vì chúng chỉ có thể được nhìn thấy trong bối cảnh của phương thức mà chúng được sử dụng, temps có xu hướng khuyến khích các phương thức dài hơn, bởi vì đó là cách duy nhất bạn có thể tiếp cận với temp. thay thế temp bằng một phương thức truy vấn, bất kỳ phương thức nào trong lớp đều có thể nhận được thông tin. Điều đó giúp ích rất nhiều trong việc đưa ra mã sạch hơn cho lớp. "

Vì vậy, có, sử dụng temps nếu họ làm cho mã dễ đọc hơn, đặc biệt nếu đó là một quy tắc địa phương cho bạn và nhóm của bạn. Nhưng hãy lưu ý rằng làm như vậy đôi khi có thể khiến bạn khó phát hiện ra những cải tiến thay thế lớn hơn. Nếu bạn có thể phát triển khả năng cảm nhận của mình khi đáng để đi mà không cần những thứ đó, và trở nên thoải mái hơn khi làm như vậy, thì đó có lẽ là một điều tốt.

FWIW Cá nhân tôi đã tránh đọc cuốn sách "Tái cấu trúc" của Fowler trong mười năm vì tôi tưởng tượng không có nhiều điều để nói về các chủ đề tương đối đơn giản như vậy. Tôi đã hoàn toàn sai. Cuối cùng khi tôi đọc nó, nó đã thay đổi các hoạt động mã hóa hàng ngày của tôi, tốt hơn nhiều.


1
Câu trả lời chính xác. Mã tốt nhất tôi đã viết (và đã thấy từ những người khác) thường có rất nhiều phương thức nhỏ (2, 3 dòng) có thể thay thế các biến tạm thời như vậy. Liên quan là điều này gây tranh cãi rằng các phương pháp riêng tư bốc mùi .... Nghĩ kỹ và tôi thường thấy nó đúng.
Stijn de Witt

Các temps trong câu hỏi này đã đề cập đến các chức năng, vì vậy câu trả lời này không liên quan đến câu hỏi của OP.
dùng949300

@ user949300 Tôi không nghĩ nó không liên quan. Có, temps trong ví dụ cụ thể của OP là các giá trị trả về từ các hàm hoặc phương thức. Nhưng OP rất rõ ràng đó chỉ là một kịch bản ví dụ. Câu hỏi thực tế là chung chung hơn nhiều "chúng ta có nên loại bỏ các biến tạm thời khi chúng ta có thể?"
Jonathan Hartley

1
OK, tôi đã bán, ** đã cố gắng ** thay đổi phiếu bầu của tôi. Nhưng thường thì khi tôi vượt quá phạm vi giới hạn của câu hỏi OP, tôi nhận được. VẬY có thể hay thay đổi ... :-). Er, không thể thay đổi phiếu bầu của tôi cho đến khi bạn chỉnh sửa, bất cứ điều gì ...
user949300

@ user949300 Bò thần! Một người thay đổi suy nghĩ của họ khi xem xét chu đáo các vấn đề! Bạn, thưa ngài, là rất hiếm, và tôi ném mũ của tôi cho bạn.
Jonathan Hartley

3

Vâng, đó là một ý tưởng tốt để loại bỏ các biến cục bộ nếu bạn có thể làm cho mã dễ đọc hơn qua đó. Cái áo lót dài đó của bạn không thể đọc được, nhưng sau đó nó đi theo một kiểu OO dài dòng. Nếu bạn có thể giảm nó thành một cái gì đó như

acquireWakeLock(POWER_SERVICE, PARTIAL, "abc");

sau đó tôi có thể nói nó có thể là một ý tưởng tốt. Có lẽ bạn có thể giới thiệu các phương thức trợ giúp để đun sôi nó xuống một cái gì đó tương tự; nếu mã như thế này xuất hiện nhiều lần thì nó có thể đáng giá.


2

Chúng ta hãy xem xét Luật của Demeter ở đây. Trong bài viết Wikipedia về LoD, phần sau đây được nêu:

Định luật Demeter cho các hàm yêu cầu một phương thức m của một đối tượng O chỉ có thể gọi các phương thức của các loại đối tượng sau: [2]

  1. Bản thân nó
  2. thông số của m
  3. Bất kỳ đối tượng được tạo / khởi tạo trong m
  4. Đối tượng thành phần trực tiếp của O
  5. Một biến toàn cục, có thể truy cập bằng O, trong phạm vi m

Một trong những hậu quả của việc tuân theo Luật này là bạn nên tránh gọi các phương thức của các đối tượng được trả về bởi các phương thức khác trong một chuỗi dài chấm với nhau, như được hiển thị trong ví dụ thứ hai ở trên:

((PowerManager)getSystemService(POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag").acquire();

Thật sự rất khó để tìm ra điều này đang làm. Để hiểu nó, bạn phải hiểu mỗi thủ tục làm gì, sau đó giải mã hàm nào đang được gọi trên đối tượng nào. Khối mã đầu tiên

PowerManager powerManager=(PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock=powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"abc");
wakeLock.acquire();

hiển thị rõ ràng những gì bạn đang cố gắng thực hiện - bạn đang nhận được một đối tượng PowerManager, nhận được một đối tượng WakeLock từ PowerManager, sau đó có được WakeLock. Ở trên tuân theo quy tắc số 3 của LoD - bạn đang khởi tạo các đối tượng này trong mã của mình và do đó có thể sử dụng chúng để hoàn thành những gì bạn cần.

Có lẽ một cách khác để suy nghĩ là hãy nhớ rằng khi tạo phần mềm, bạn nên viết cho rõ ràng, không phải cho ngắn gọn. 90% tất cả sự phát triển phần mềm là bảo trì. Không bao giờ viết một đoạn mã bạn sẽ không vui lòng duy trì.

May mắn nhất.


3
Tôi muốn biết làm thế nào cú pháp chất lỏng phù hợp với câu trả lời này. Với định dạng chính xác, cú pháp chất lỏng rất dễ đọc.
Gusdor

12
Đó không phải là "Luật của Demeter" nghĩa là gì. Luật Demeter không phải là một bài tập đếm chấm; về cơ bản nó có nghĩa là "chỉ nói chuyện với bạn bè ngay lập tức của bạn."
Robert Harvey

5
Truy cập các phương thức và thành viên tương tự thông qua một biến trung gian vẫn vi phạm luật Demeter nghiêm trọng. Bạn vừa che khuất nó, không sửa nó.
Jonathan Hartley

2
Xét về "Luật của Demeter", cả hai phiên bản đều "xấu" như nhau.
Hulk

@Gusdor đồng ý, đây là một nhận xét về kiểu dáng trong ví dụ câu hỏi. Câu hỏi đã được chỉnh sửa.
Jodrell

2

Một điều cần lưu ý là mã được đọc thường xuyên, đến các "độ sâu" khác nhau. Mã này:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc");
wakeLock.acquire();

rất dễ "lướt qua". Đó là 3 tuyên bố. Đầu tiên chúng ta đến với a PowerManager. Sau đó, chúng tôi đến với a WakeLock. Sau đó chúng tôi acquirecác wakeLock. Tôi có thể thấy điều đó thực sự dễ dàng chỉ bằng cách nhìn vào điểm bắt đầu của mỗi dòng; các bài tập biến đơn giản thực sự dễ dàng nhận ra một phần là "Gõ varName = ..." và chỉ lướt qua "...". Tương tự, câu lệnh cuối cùng rõ ràng không phải là hình thức của bài tập, nhưng nó chỉ liên quan đến hai tên, vì vậy "ý chính" là rõ ràng ngay lập tức. Thường thì đó là tất cả những gì tôi cần biết nếu tôi chỉ cố gắng trả lời "mã này làm gì?" ở mức độ cao

Nếu tôi đang theo đuổi một lỗi tinh vi mà tôi nghĩ là ở đây, thì rõ ràng tôi sẽ cần phải xem xét vấn đề này chi tiết hơn nhiều, và thực sự sẽ nhớ "..." s. Nhưng cấu trúc câu lệnh riêng biệt vẫn giúp tôi thực hiện một câu lệnh đó tại một thời điểm (đặc biệt hữu ích nếu tôi cần đi sâu hơn vào việc thực hiện những điều được gọi trong mỗi câu lệnh; khi tôi quay lại tôi đã hiểu đầy đủ "một đơn vị" và sau đó có thể chuyển sang tuyên bố tiếp theo).

((PowerManager)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag")
    .acquire();

Bây giờ tất cả chỉ là một tuyên bố. Cấu trúc cấp cao nhất không dễ đọc lướt qua; trong phiên bản gốc của OP, không có ngắt dòng và thụt lề để giao tiếp trực quan với bất kỳ cấu trúc nào, tôi sẽ phải đếm các dấu ngoặc đơn để giải mã nó thành một chuỗi gồm 3 bước. Nếu một số biểu thức đa phần được lồng vào nhau thay vì được sắp xếp như một chuỗi các lệnh gọi phương thức, thì nó vẫn có thể trông giống như thế này, vì vậy tôi phải cẩn thận khi tin vào điều đó mà không tính dấu ngoặc đơn. Và nếu tôi tin tưởng vào vết lõm và chỉ lướt qua điều cuối cùng là điểm được cho là của tất cả những điều này, thì điều gì sẽ .acquire()nói với tôi về chính nó?

Đôi khi, mặc dù, đó có thể là những gì bạn muốn. Nếu tôi áp dụng nửa chuyển đổi của bạn và viết:

WakeLock wakeLock =
     ((PowerManeger)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLockTage");
wakeLock.acquire();

Bây giờ điều này giao tiếp với một skim nhanh "có được WakeLock, sau đó acquirenó". Thậm chí đơn giản hơn phiên bản đầu tiên. Rõ ràng ngay lập tức rằng thứ được mua là a WakeLock. Nếu việc lấy PowerManagerchỉ là một chi tiết phụ không đáng kể so với điểm của mã này, nhưng điều wakeLockquan trọng là, thì nó thực sự có thể giúp chôn vùi những PowerManagerthứ đó để lướt qua nó nếu bạn chỉ cố gắng nhanh chóng lướt qua nó. ý tưởng về những gì mã này làm. Và không đặt tên nó truyền đạt rằng nó chỉ sử dụng một lần, và đôi khi đó là Điều quan trọng (nếu phần còn lại của phạm vi rất dài, tôi phải đọc tất cả những điều đó để biết liệu nó có được sử dụng lại hay không, mặc dù sử dụng phạm vi phụ rõ ràng có thể là một cách khác để giải quyết vấn đề đó, nếu ngôn ngữ của bạn hỗ trợ chúng).

Điều này dẫn đến tất cả phụ thuộc vào bối cảnh và những gì bạn muốn giao tiếp. Cũng như viết văn xuôi bằng ngôn ngữ tự nhiên, luôn có nhiều cách để viết một đoạn mã nhất định về cơ bản tương đương về mặt nội dung thông tin. Cũng như viết văn xuôi bằng ngôn ngữ tự nhiên, cách bạn chọn giữa chúng thường không nên áp dụng các quy tắc cơ học như "loại bỏ bất kỳ biến cục bộ nào chỉ xảy ra một lần". Thay vì như thế nàobạn chọn viết ra mã của bạn sẽ nhấn mạnh những điều nhất định và không nhấn mạnh những điều khác. Bạn nên cố gắng thực hiện những lựa chọn đó một cách có ý thức (bao gồm cả lựa chọn viết mã ít đọc hơn vì lý do kỹ thuật đôi khi), dựa trên những gì bạn thực sự muốn nhấn mạnh. Đặc biệt hãy suy nghĩ về những gì sẽ phục vụ những độc giả chỉ cần "lấy ý chính" của mã của bạn (ở nhiều cấp độ khác nhau), vì điều đó sẽ xảy ra thường xuyên hơn nhiều so với cách đọc biểu thức rất gần.


Đối với tôi, mặc dù tôi không có kiến ​​thức về API, nhưng rõ ràng mã này làm gì, thậm chí không có các biến. getSystemService(POWER_SERVICE)được người quản lý quyền lực. .newWakeLockđược khóa thức dậy. .acquiremua lại nó OK, tôi không biết tất cả các loại, nhưng tôi có thể tìm thấy nó trong IDE nếu tôi cần.
rghome

Nó có thể rõ ràng với bạn những gì mã được cho là để làm , nhưng bạn không biết nó thực sự làm gì. Nếu tôi đang tìm kiếm một lỗi, tất cả những gì tôi biết là một sốở đâu đó không làm những gì nó phải làm và tôi phải tìm mã nào.
gnasher729

@ gnasher729 Có. Săn bọ là một ví dụ tôi đã đưa ra khi bạn cần đọc kỹ tất cả các chi tiết. Và một trong những điều thực sự giúp tìm ra mã không làm những gì nó phải làm là có thể dễ dàng thấy ý định của tác giả ban đầu là gì.
Ben

Khi tôi trở ra, tôi hoàn toàn hiểu "một đơn vị" và sau đó có thể chuyển sang tuyên bố tiếp theo. Thật ra là không. Trong thực tế các biến cục bộ ngăn chặn sự hiểu biết đầy đủ. Bởi vì họ vẫn còn vương vấn ... chiếm không gian tinh thần ... điều gì sẽ xảy ra với họ trong 20 tuyên bố tiếp theo ... đánh trống ... Nếu không có người dân địa phương, bạn sẽ chắc chắn rằng các vật thể đã biến mất và không thể làm gì với họ trong các dòng tiếp theo. Không cần phải nhớ về họ. Tôi muốn returncàng nhanh từ các phương pháp càng tốt, vì lý do tương tự.
Stijn de Witt

2

Đây thậm chí còn là một antipotype có tên riêng: Train Wreck . Một số lý do để tránh sự thay thế đã được nêu:

  • Khó đọc hơn
  • Khó gỡ lỗi hơn (cả để xem các biến và phát hiện các vị trí ngoại lệ)
  • Vi phạm Luật Demeter (LoD)

Xem xét nếu phương pháp này biết quá nhiều từ các đối tượng khác. Phương pháp xâu chuỗi là một phương án có thể giúp bạn giảm khớp nối.

Cũng nên nhớ rằng các tài liệu tham khảo đến các đối tượng là thực sự rẻ.


Ehm không phải là mã sửa đổi làm phương pháp chuỗi? EDIT đọc bài viết được liên kết để tôi thấy sự khác biệt bây giờ. Bài viết khá tốt cảm ơn!
Stijn de Witt

Cảm ơn đã xem xét nó. Dù sao, chuỗi phương thức có thể hoạt động trong một số trường hợp trong khi chỉ cần để lại biến có thể là quyết định thích hợp. Thật tốt khi biết lý do tại sao mọi người không đồng ý với câu trả lời của bạn.
Borjab

Tôi đoán ngược lại với "Train Wreck" là "Local Line". Đó là chuyến tàu giữa hai thành phố lớn dừng ở mọi làng quê ở giữa chỉ trong trường hợp ai đó muốn lên hoặc xuống, mặc dù hầu hết các ngày không có ai làm.
rghome

1

Câu hỏi đã nêu là "Chúng ta có nên loại bỏ các biến cục bộ nếu có thể"?

Không, bạn không nên loại bỏ chỉ vì bạn có thể.

Bạn nên làm theo hướng dẫn mã hóa trong cửa hàng của bạn.

Đối với hầu hết các biến cục bộ làm cho mã dễ đọc và gỡ lỗi hơn.

Tôi thích những gì bạn đã làm với PowerManager powerManager. Đối với tôi một trường hợp thấp hơn của lớp có nghĩa là nó chỉ là một lần sử dụng.

Khi bạn không nên là nếu biến sẽ giữ các tài nguyên đắt tiền. Nhiều ngôn ngữ có cú pháp cho một biến cục bộ cần được dọn sạch / phát hành. Trong C # nó đang sử dụng.

using(SQLconnection conn = new SQLconnnection())
{
    using(SQLcommand cmd = SQLconnnection.CreateCommand())
    {
    }
}

Điều này không thực sự liên quan đến các biến cục bộ mà là dọn dẹp tài nguyên ... Tôi không rành về C # nhưng tôi sẽ ngạc nhiên nếu điều using(new SQLConnection()) { /* ... */ }đó cũng không hợp pháp (liệu nó có hữu ích hay không là một vấn đề khác :).
Stijn de Witt

1

Một khía cạnh quan trọng đã không được đề cập trong các câu trả lời khác: Bất cứ khi nào bạn thêm một biến, bạn giới thiệu trạng thái có thể thay đổi. Điều này thường là một điều xấu vì nó làm cho mã của bạn phức tạp hơn và do đó khó hiểu và kiểm tra hơn. Tất nhiên, phạm vi của biến càng nhỏ, vấn đề càng nhỏ.

Những gì bạn muốn thực sự không phải là một biến có giá trị có thể được sửa đổi mà là một hằng số tạm thời. Do đó, xem xét để thể hiện điều này trong mã của bạn nếu ngôn ngữ của bạn cho phép nó. Trong Java bạn có thể sử dụng finalvà trong C ++, bạn có thể sử dụng const. Trong hầu hết các ngôn ngữ chức năng, đây là hành vi mặc định.

Đúng là các biến cục bộ là loại ít gây hại nhất của trạng thái đột biến. Các biến thành viên có thể gây ra nhiều rắc rối hơn và các biến tĩnh thậm chí còn tồi tệ hơn. Tuy nhiên, tôi thấy điều quan trọng là thể hiện chính xác nhất có thể trong mã của bạn những gì nó phải làm. Và có một sự khác biệt rất lớn giữa một biến có thể được sửa đổi sau này và một tên đơn thuần cho một kết quả trung gian. Vì vậy, nếu ngôn ngữ của bạn cho phép bạn thể hiện sự khác biệt này, chỉ cần làm điều đó.


2
Tôi không đánh giá thấp câu trả lời của bạn nhưng tôi đoán rằng nó liên quan đến thực tế là các biến cục bộ thường không được coi là 'trạng thái' trong các ngôn ngữ bắt buộc trừ khi bạn đang nói về một phương pháp chạy dài nào đó. Tôi đồng ý về việc sử dụng Final / const như một cách thực hành tốt nhất nhưng không chắc rằng việc đưa ra một biến để đơn giản phá vỡ một cuộc gọi bị xiềng xích sẽ dẫn đến các vấn đề gây ra bởi trạng thái đột biến. Trong thực tế, việc sử dụng các biến cục bộ giúp đối phó với trạng thái đột biến. Nếu một phương thức có thể trả về các giá trị khác nhau vào các thời điểm khác nhau, bạn có thể gặp phải một số lỗi thực sự thú vị khác.
JimmyJames

@JimmyJames: Đã thêm một số lời giải thích cho câu trả lời của tôi. Nhưng có một phần bình luận của bạn mà tôi không hiểu: "việc sử dụng các biến cục bộ giúp xử lý trạng thái có thể thay đổi". Bạn có thể giải thích điều này?
Frank Puffer

1
Ví dụ: giả sử tôi cần kiểm tra một giá trị và nếu nó trên 10 phát ra cảnh báo. Giả sử giá trị này đến từ phương thức getFoo(). Nếu tôi tránh khai báo cục bộ, tôi sẽ kết thúc với if(getFoo() > 10) alert(getFoo());But getFoo () có thể trả về các giá trị khác nhau trên hai cuộc gọi khác nhau. Tôi có thể gửi một cảnh báo có giá trị nhỏ hơn hoặc bằng 10, điều này gây nhầm lẫn ở mức tối đa và sẽ trở lại với tôi như một khiếm khuyết. Loại điều này trở nên quan trọng hơn với sự tương tranh. Nhiệm vụ địa phương là nguyên tử.
JimmyJames

Điểm rất tốt. Có lẽ những lý do tôi không thích những người dân địa phương .... Bạn sẽ làm điều gì đó với PowerManagerví dụ sau khi gọi phương pháp này? Để tôi kiểm tra phần còn lại của phương pháp .... mmm ok no. Và điều này WakeLockbạn có ... bạn đang làm gì với điều đó (quét phần còn lại của phương thức một lần nữa) ... mmm lại không có gì ... ok vậy tại sao họ lại ở đó? Ồ vâng, đáng lẽ phải dễ đọc hơn ... nhưng không có chúng tôi chắc chắn bạn không sử dụng chúng nữa nên tôi không phải đọc phần còn lại của phương pháp.
Stijn de Witt

Trong một cuộc nói chuyện gần đây, diễn giả đã lập luận rằng cách tốt nhất để chỉ ra rằng một biến là cuối cùng là viết các phương thức ngắn trong đó rõ ràng là biến đó không thay đổi . Nếu finalcần thiết cho một biến cục bộ trong một phương thức, phương thức của bạn quá dài.
dùng949300
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.