Khả năng duy trì của logic Boolean - Có được lồng nếu các câu lệnh cần không?


11

Cái nào trong số này là tốt hơn cho khả năng bảo trì?

if (byteArrayVariable != null)
    if (byteArrayVariable .Length != 0)
         //Do something with byteArrayVariable 

HOẶC LÀ

if ((byteArrayVariable != null) && (byteArrayVariable.Length != 0))
  //Do something with byteArrayVariable 

Tôi thích đọc và viết thứ hai, nhưng tôi nhớ việc đọc mã hoàn thành rằng làm những việc như thế là không tốt cho khả năng bảo trì.

Điều này là do bạn đang dựa vào ngôn ngữ để không đánh giá phần thứ hai ifnếu phần thứ nhất là sai và không phải tất cả các ngôn ngữ đều làm điều đó. (Phần thứ hai sẽ đưa ra một ngoại lệ nếu được đánh giá bằng null byteArrayVariable.)

Tôi không biết đó có thực sự là điều đáng lo ngại hay không và tôi muốn phản hồi chung về câu hỏi.

Cảm ơn.


1
Mẫu đầu tiên có nguy cơ lơ lửng - khác
JBRWilkinson

Chỉ tò mò, bạn đang làm việc với ngôn ngữ nào?
STW

@STW - chúng tôi sử dụng C # và có một số mã kế thừa trong Delphi.
Núi lửa

2
tốt để biết Tôi không quen thuộc với Delphi nhưng trong C #, bạn có thể tin tưởng vào &&và các ||nhà khai thác thực hiện đánh giá ngắn mạch. Đi từ ngôn ngữ này sang ngôn ngữ khác thường có một số vấn đề, và thật tốt khi làm công việc đánh giá mã để giúp nắm bắt những vấn đề nhỏ đó.
STW

Trong khi cùng với nhiều câu trả lời ở đây tôi thấy cái thứ hai dễ đọc hơn, cái thứ nhất dễ gỡ lỗi hơn nhiều. Trong trường hợp này, tôi vẫn sử dụng cái thứ hai vì bạn đang kiểm tra dễ dàng để gỡ lỗi mọi thứ. Nhưng nói chung hãy cẩn thận.
Magus

Câu trả lời:


30

Tôi nghĩ rằng hình thức thứ hai là tốt, và cũng thể hiện rõ hơn những gì bạn đang cố gắng làm. Bạn nói thế...

Tôi nhớ lại việc đọc mã hoàn thành rằng làm những việc như thế là không tốt cho khả năng bảo trì. Điều này là do bạn đang dựa vào ngôn ngữ để không đánh giá phần thứ hai nếu nếu phần thứ nhất là sai và không phải tất cả các ngôn ngữ đều làm điều đó.

Không thành vấn đề nếu tất cả các ngôn ngữ làm điều đó. Bạn đang viết trong một ngôn ngữ cụ thể, vì vậy nó chỉ quan trọng nếu ngôn ngữ nào đó. Mặt khác, về cơ bản, bạn nói rằng bạn không nên sử dụng các tính năng của một ngôn ngữ cụ thể vì các ngôn ngữ khác có thể không hỗ trợ các tính năng đó và điều đó thật ngớ ngẩn.


12
Đã đồng ý. Tại sao trên trái đất tôi sẽ quan tâm nếu mã của tôi sẽ chạy như vậy khi được sao chép sang ngôn ngữ khác?
Tim Goodman

16

Tôi ngạc nhiên không ai đề cập đến nó (vì vậy hy vọng tôi không đọc sai câu hỏi) - nhưng cả hai loại đều tệ!

Cách thay thế tốt hơn sẽ là sử dụng lối thoát sớm (đặt câu lệnh trả về sớm trong mã nếu không có mã nào khác thực thi). Điều này làm cho giả định rằng mã của bạn được tái cấu trúc tương đối tốt và mỗi phương thức có một mục đích ngắn gọn.

Tại thời điểm đó, bạn sẽ làm một cái gì đó như:

if ((byteArrayVariable == null) || (byteArrayVariable.Length != 0)) {
  return; // nothing to be done, leaving
}

// Conditions met, do something

Nó có thể trông giống như xác nhận - và đó chính xác là những gì nó được. Nó xác nhận rằng các điều kiện đã được thỏa mãn cho phương thức để thực hiện điều đó - cả hai tùy chọn bạn đưa ra đều ẩn logic thực tế trong các kiểm tra tiền điều kiện.

Nếu mã của bạn là rất nhiều spaghetti (phương pháp loooong, rất nhiều if lồng nhau với logic nằm rải rác giữa chúng, v.v.) thì bạn nên tập trung vào vấn đề lớn hơn là làm cho mã của bạn thành hình trước các vấn đề về kiểu dáng nhỏ hơn. Tùy thuộc vào môi trường của bạn và mức độ nghiêm trọng của mớ hỗn độn, có một số công cụ tuyệt vời để trợ giúp (ví dụ Visual Studio có chức năng tái cấu trúc "Phương pháp trích xuất").


Như Jim đề cập, vẫn còn chỗ để tranh luận về cách cấu trúc lối ra sớm. Thay thế cho những gì ở trên sẽ là:

if (byteArrayVariable == null) 
  return; // nothing to be done, leaving

if (byteArrayVariable.Length != 0)
  return;

// Conditions met, do something

1
Tôi đồng ý với sự trở lại thay vì gói logic tốt, nhưng những người tương tự sẽ sử dụng cùng một lý lẽ về việc sử dụng | | như đối với &&.
MIA

14

Đừng phí thời gian cố gắng để bẫy cho mỗi phi này điều kiện. Nếu bạn có thể không đủ tiêu chuẩn dữ liệu hoặc điều kiện thì hãy làm điều đó càng sớm càng tốt.

if (byteArrayVariable == null) Exit;
if (byteArrayVariable.Length == 0) Exit;
// do something

Điều này cho phép bạn điều chỉnh mã của mình dễ dàng hơn nhiều cho các điều kiện mới

if (byteArrayVariable == null) Exit;
if (byteArrayVariable.Length == 0) Exit;
if (NewCondition == false) Exit;
if (NewerCondition == true) Exit;
// do something

2
+1 Yup, yup, yup. Logic đơn giản như thế này sẽ không dẫn đến mã khó. Ruột của bạn (người hỏi) nên nói với bạn điều này.
Công việc

11

Ví dụ của bạn là ví dụ hoàn hảo về lý do tại sao lồng nhau ifslà một cách tiếp cận tồi cho hầu hết các ngôn ngữ hỗ trợ đúng so sánh boolean. Lồng nhau ifstạo ra một tình huống mà ý định của bạn không rõ ràng. Có yêu cầu rằng cái thứ hai ifngay sau cái thứ nhất? Hay đó chỉ là do bạn chưa đặt một hoạt động khác vào đó?

if ((byteArrayVariable != null) && (byteArrayVariable.Length != 0))
  //Do something with byteArrayVariable 

Trong trường hợp này, những điều này khá nhiều phải được cùng nhau. Họ đóng vai trò là người bảo vệ để đảm bảo bạn không thực hiện một thao tác dẫn đến ngoại lệ. Sử dụng lồng nhau ifs, bạn để nó giải thích cho người theo dõi bạn về việc hai người đại diện cho một người bảo vệ hai phần hoặc một số logic phân nhánh khác. Bằng cách kết hợp chúng thành một đơn, iftôi nói rõ điều đó. Thật khó khăn hơn nhiều để lén lút một hoạt động bên trong nơi không nên đến.

Tôi sẽ luôn ủng hộ việc truyền đạt rõ ràng về ý định của tôi đối với khẳng định ngu ngốc của ai đó rằng nó "không hoạt động trong tất cả các ngôn ngữ". Đoán xem ... throwcũng không hoạt động trong tất cả các ngôn ngữ, điều đó có nghĩa là tôi không nên sử dụng nó khi mã hóa bằng C # hoặc Java và thay vào đó nên dựa vào các giá trị trả về để báo hiệu khi có sự cố?

Tất cả những gì đã nói, việc đảo ngược nó sẽ dễ đọc hơn rất nhiều và chỉ cần trả lại phương thức nếu không hài lòng như STW nói trong câu trả lời của họ.


Nhiều hơn hai điều kiện có thể đảm bảo chức năng của chính họ trả về null.
Công việc

3

Trong trường hợp này, hai mệnh đề của bạn có liên quan trực tiếp với nhau, vì vậy hình thức thứ 2 là thích hợp hơn.

Nếu chúng không liên quan (ví dụ: một loạt các mệnh đề bảo vệ ở các điều kiện khác nhau) thì tôi thích dạng thứ nhất (hoặc tôi sẽ cấu trúc lại một phương thức với một tên đại diện cho điều kiện ghép thực sự đại diện cho cái gì).


1

Nếu bạn làm việc ở Delphi, cách đầu tiên, nói chung, an toàn hơn. (Đối với ví dụ đã cho, sẽ không có nhiều lợi ích khi sử dụng ifs lồng nhau.)

Delphi cho phép bạn chỉ định xem biểu thức boolean có đánh giá hay "đoản mạch" hay không, thông qua các công tắc biên dịch. Thực hiện cách số 1 (ifs lồng nhau) cho phép bạn đảm bảo rằng mệnh đề thứ hai thực thi khi và chỉ khi (và đúng sau ) mệnh đề đầu tiên đánh giá là đúng.


1
Trong 23 năm phát triển Delphi, tôi chưa bao giờ thay đổi tùy chọn "Complete boolean eval". Nếu tôi gửi mã ở nơi khác, tôi sẽ đặt {$ BOOLEVAL OFF} hoặc {$ B-} trong đơn vị.
Gerry

Tôi cũng không. Tôi cũng luôn sử dụng kiểm tra phạm vi ... nhưng những người khác thì không. Và nó không nên tạo ra sự khác biệt ngoại trừ có lẽ từ góc độ hiệu suất vì bạn không nên sử dụng các hiệu ứng phụ trong các hàm được sử dụng trong các biểu thức boolean. Tôi chắc chắn có ai đó ngoài đó, mặc dù!
Frank Shearar

@Gerry: Đồng ý. Lý do duy nhất bạn có thể muốn đánh giá đầy đủ là tác dụng phụ - và tác dụng phụ trong điều kiện là một ý tưởng tồi!
Loren Pechtel

Chỉ cần đọc lại bình luận của tôi 23 năm là 13! Tôi không có Tardis
Gerry

1

Kiểu thứ hai có thể phản tác dụng trong VBA vì nếu thử nghiệm đầu tiên là nếu đối tượng được tạo, nó vẫn sẽ thực hiện thử nghiệm thứ hai và lỗi. Tôi đã có thói quen trong VBA khi xử lý xác minh đối tượng để luôn sử dụng phương thức đầu tiên.


2
Đó là bởi vì VBA là một ngôn ngữ tồi tệ
Falmarri

@Falmarri, nó có một số điều khủng khiếp về nó (và một số điều tốt). Tôi sẽ sửa chữa rất nhiều thứ nếu tôi có máy khoan.

2
Việc thiếu đánh giá Boolean ngắn mạch là một điều di sản hơn bất cứ điều gì. Không biết tại sao họ không bắt đầu với nó. "Sửa lỗi" VB.NET của OrElse và AndAlso hơi khó chịu, nhưng có lẽ là lựa chọn duy nhất để đối phó với nó mà không phá vỡ tính tương thích ngược.
JohnFx

1

Biểu mẫu thứ hai dễ đọc hơn, đặc biệt nếu bạn đang sử dụng các khối {} xung quanh mỗi mệnh đề, bởi vì hình thức đầu tiên sẽ dẫn đến các khối {} lồng nhau và nhiều cấp độ thụt đầu dòng, có thể gây khó đọc (bạn phải đếm không gian thụt lề để tìm ra nơi mỗi khối kết thúc).


0

Tôi thấy cả hai đều có thể đọc được và tôi không chấp nhận lập luận rằng bạn nên lo lắng về khả năng duy trì mã nếu bạn chuyển đổi ngôn ngữ.

Trong một số ngôn ngữ, tùy chọn thứ hai thực hiện tốt hơn vì vậy nó sẽ là lựa chọn rõ ràng sau đó.


0

Tôi với bạn; Tôi làm theo cách thứ hai. Đối với tôi, nó rõ ràng hơn về mặt logic. Mối lo ngại rằng một số ngôn ngữ sẽ thực thi phần thứ hai ngay cả khi phần thứ nhất đánh giá sai có vẻ hơi ngớ ngẩn đối với tôi, vì trong đời tôi chưa bao giờ viết một chương trình chạy bằng "một số ngôn ngữ"; mã của tôi dường như luôn tồn tại trong một ngôn ngữ cụ thể, có thể xử lý việc xây dựng đó hoặc không thể. (Và nếu tôi không chắc liệu ngôn ngữ cụ thể có thể xử lý nó hay không, đó là thứ tôi thực sự cần học, phải không.)

Trong tâm trí tôi, mối nguy hiểm với cách làm đầu tiên là nó khiến tôi dễ bị vô tình đặt mã bên ngoài ifkhối lồng nhau khi tôi không có ý định. Điều đó, thừa nhận, hầu như không phải là một mối quan tâm lớn, nhưng có vẻ hợp lý hơn là băn khoăn liệu mã của tôi có phải là bất khả tri ngôn ngữ hay không.


0

Tôi thích tùy chọn thứ hai, vì nó dễ đọc hơn.

Nếu logic bạn đang triển khai phức tạp hơn (kiểm tra xem một chuỗi không rỗng và không trống chỉ là một ví dụ), thì bạn có thể thực hiện tái cấu trúc hữu ích: trích xuất hàm boolean. Tôi với @mipadi về nhận xét "Hoàn thành mã" ("không thành vấn đề nếu tất cả các ngôn ngữ làm điều đó ...") Nếu người đọc mã của bạn không quan tâm đến việc tìm kiếm bên trong hàm được trích xuất, thì thứ tự trong đó các toán hạng boolean được đánh giá trở thành một câu hỏi tranh luận với chúng.


0
if ((byteArrayVariable != null)
 && (byteArrayVariable.Length != 0)) {
    //Do something with byteArrayVariable 

nhưng về cơ bản đó là lựa chọn thứ hai.

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.