Nếu thang khác được cho là bắt tất cả các điều kiện - có nên thêm một mệnh đề cuối cùng dư thừa không?


10

Đây là một điều tôi đang làm rất nhiều gần đây.

Thí dụ:

setCircle(circle, i, { current }) {
    if (i == current) {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
    } else if (i < current) {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
    } else if (i > current) {
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    }
}

Thông thường, thang if / other phức tạp hơn đáng kể so với ...

Xem điều khoản cuối cùng? Nó là dư thừa. Các bậc thang được cho là cuối cùng bắt tất cả các điều kiện có thể. Do đó, nó có thể được viết lại như thế:

setCircle(circle, i, { current }) {
    if (i == current) {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
    } else if (i < current) {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
    } else {
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    }
}

Đây là cách tôi thường viết mã, nhưng tôi không thích phong cách này. Khiếu nại của tôi là điều kiện theo đó phần cuối của mã sẽ được thực thi không rõ ràng từ mã. Do đó tôi bắt đầu viết điều kiện này một cách rõ ràng để làm cho nó rõ ràng hơn.

Tuy nhiên:

  • Hoàn toàn viết ra điều kiện đầy đủ cuối cùng là ý tưởng của riêng tôi và tôi có những trải nghiệm tồi tệ với ý tưởng của riêng mình - thường mọi người hét lên với tôi về những gì tôi đang làm là khủng khiếp - và (đôi khi nhiều) sau đó tôi phát hiện ra rằng đó thực sự là tối ưu;
  • Một gợi ý tại sao điều này có thể là một ý tưởng tồi: Không thể áp dụng cho Javascript, nhưng trong các ngôn ngữ khác, trình biên dịch có xu hướng đưa ra cảnh báo hoặc thậm chí lỗi về việc kiểm soát đến cuối chức năng. Gợi ý làm điều gì đó như thế có thể không quá phổ biến hoặc tôi đang làm sai.
    • Trình biên dịch khiếu nại khiến tôi đôi khi viết điều kiện cuối cùng trong một bình luận, nhưng tôi đoán làm như vậy thật kinh khủng vì các bình luận, không giống như mã, không ảnh hưởng đến ngữ nghĩa chương trình thực tế:
    } else { // i > current
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    }

Tui bỏ lỡ điều gì vậy? Hoặc nó có ổn để làm những gì tôi mô tả hay đó là một ý tưởng tồi?


+1 cho câu trả lời thực thi của @ Barshe rằng sự dư thừa trong mã làm tăng cơ hội cho các khiếm khuyết. Cũng có một vấn đề hiệu quả thấp hơn nhiều . Để mở rộng về điều đó, liên quan đến ví dụ mã đang được đề cập, chúng ta có thể viết một loạt if-if-if-as as as if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if if thay vì một loạt các điều kiện độc lập (cũng sẽ kém hiệu quả hơn, vì điều đó nói rằng tất cả các điều kiện nên được đánh giá ngay cả sau một trận đấu).
Erik Eidt

@ErikEidt> vì điều đó nói rằng tất cả các điều kiện nên được đánh giá ngay cả sau khi khớp với +1 trừ khi bạn quay lại từ một hàm hoặc "phá vỡ" từ một vòng lặp (không phải là trường hợp trong ví dụ trên).
Darek Nędza

1
+1, Câu hỏi hay. Như một bên có thể bạn quan tâm rằng với sự hiện diện của NaN, ví dụ này không đầy đủ.
monocell

Câu trả lời:


6

Cả hai cách tiếp cận đều hợp lệ. Nhưng chúng ta hãy xem xét kỹ hơn về ưu và nhược điểm.

Đối với một if-chain với các điều kiện tầm thường như ở đây, điều đó không thực sự quan trọng:

  • với một trận chung kết else, rõ ràng người đọc sẽ tìm ra trong điều kiện nào khác được kích hoạt;
  • với một trận chung kết else if, điều hiển nhiên đối với người đọc là không elsecần thêm vì bạn đã bao quát tất cả.

Tuy nhiên, có rất nhiều if-chains dựa vào các điều kiện phức tạp hơn, kết hợp các trạng thái của một số biến, có lẽ với một biểu thức logic phức tạp. Trong trường hợp này thì ít rõ ràng hơn. Và đây là hậu quả của mỗi phong cách:

  • cuối cùng else: bạn chắc chắn rằng một trong những chi nhánh đã được thực hiện. Nếu bạn quên một trường hợp, nó sẽ đi qua nhánh cuối cùng đó, vì vậy trong quá trình gỡ lỗi, nếu nhánh cuối cùng được chọn và bạn mong đợi điều gì khác, bạn sẽ nhanh chóng tìm ra.
  • cuối cùng else if: bạn cần lấy điều kiện dự phòng để viết mã và điều này tạo ra một nguồn lỗi tiềm ẩn với việc không bao gồm tất cả các trường hợp. Hơn nữa, nếu bạn đã bỏ lỡ một trường hợp, sẽ không có gì được thực hiện và sẽ khó khăn hơn khi phát hiện ra rằng thiếu một cái gì đó (ví dụ: nếu một số biến mà bạn dự kiến ​​sẽ được đặt giữ các giá trị từ các lần lặp trước).

Vì vậy, điều kiện dư thừa cuối cùng là một nguồn rủi ro. Đây là lý do tại sao tôi muốn đề nghị đi đến một trận chung kết else.

Chỉnh sửa: mã hóa độ tin cậy cao

Nếu bạn đang phát triển với độ tin cậy cao, bạn có thể quan tâm đến một biến thể khác: hoàn thành trận chung kết rõ ràng dư thừa của bạn else ifvới trận chung kết elseđể nắm bắt mọi tình huống bất ngờ.

Đây là mã hóa phòng thủ. Nó được khuyến nghị bởi một số thông số kỹ thuật bảo mật như SEI CERT hoặc MISRA . Một số công cụ phân tích tĩnh thậm chí thực hiện điều này như một quy tắc được kiểm tra một cách có hệ thống (điều này có thể giải thích các cảnh báo trình biên dịch của bạn).


7
Điều gì sẽ xảy ra nếu sau điều kiện dự phòng cuối cùng, tôi thêm một điều kiện khác luôn ném ra một ngoại lệ có nội dung "Bạn không được phép tiếp cận tôi!" - Nó sẽ làm giảm bớt một số vấn đề trong cách tiếp cận của tôi?
gaazkam

3
@gaazkam vâng, đây là một kiểu mã hóa rất phòng thủ. Vì vậy, bạn vẫn cần tính toán điều kiện cuối cùng, nhưng đối với các chuỗi dễ bị lỗi phức tạp, ít nhất bạn sẽ tìm ra nhanh chóng. Vấn đề duy nhất tôi thấy với biến thể này là nó hơi quá mức cho trường hợp rõ ràng.
Christophe

@gaazkam Tôi đã chỉnh sửa câu trả lời của tôi để địa chỉ cũng ý tưởng bổ sung của bạn đề cập trong bình luận của bạn
Christophe

1
@gaazkam Ném một ngoại lệ là tốt. Nếu bạn làm như vậy, đừng quên bao gồm giá trị bất ngờ trong tin nhắn. Nó có thể khó tái tạo và giá trị bất ngờ có thể cung cấp manh mối về nguồn gốc của vấn đề.
Martin Maat

1
Một lựa chọn khác là đặt một asserttrong trận chung kết else if. Hướng dẫn phong cách của bạn có thể khác nhau về việc đó có phải là một ý tưởng tốt hay không, tuy nhiên. (Điều kiện assertkhông bao giờ là sai, trừ khi lập trình viên làm hỏng. Vì vậy, điều này ít nhất là sử dụng tính năng cho mục đích của nó. Nhưng rất nhiều người lạm dụng nó mà rất nhiều cửa hàng đã cấm hoàn toàn.)
Kevin

5

Một cái gì đó còn thiếu trong các câu trả lời cho đến nay là vấn đề loại thất bại nào ít gây hại hơn.

Nếu logic của bạn tốt thì điều đó không thực sự quan trọng với những gì bạn làm, trường hợp quan trọng là điều gì sẽ xảy ra nếu bạn gặp lỗi.

Bạn bỏ qua điều kiện cuối cùng: Tùy chọn cuối cùng thực thi ngay cả khi đó không phải là điều đúng.

Bạn chỉ cần thêm điều kiện cuối cùng: Nó không thực hiện bất kỳ tùy chọn nào, tùy thuộc vào tình huống, điều này có thể đơn giản có nghĩa là một cái gì đó không hiển thị (tác hại thấp) hoặc có thể có nghĩa là một ngoại lệ tham chiếu null tại một số điểm sau đó (có thể là gỡ lỗi đau đớn.)

Bạn thêm điều kiện cuối cùng và một ngoại lệ: Nó ném.

Bạn phải quyết định lựa chọn nào là tốt nhất. Trong mã phát triển tôi coi đây là một kẻ không có trí tuệ - hãy lấy trường hợp thứ ba. Tuy nhiên, tôi có thể sẽ đặt circle.src thành hình ảnh lỗi và circle. Tạm dừng thông báo lỗi trước khi ném - trong trường hợp ai đó quyết định tắt các xác nhận sau đó, điều này làm cho nó thất bại.

Một điều khác cần xem xét - các tùy chọn phục hồi của bạn là gì? Đôi khi bạn không có đường dẫn phục hồi. Những gì tôi nghĩ là ví dụ cuối cùng của việc này là lần phóng đầu tiên của tên lửa Ariane V. Đã xảy ra lỗi / 0 (thực sự là lỗi tràn bộ phận) dẫn đến việc phá hủy bộ tăng áp. Trong thực tế, mã bị lỗi không phục vụ mục đích gì vào thời điểm đó, nó đã trở thành động lực ngay lập tức khi những tên lửa đẩy dây đeo bật sáng. Khi chúng sáng lên quỹ đạo hoặc bùng nổ, bạn làm hết sức có thể, các lỗi không thể được cho phép. (Nếu tên lửa đi chệch hướng do điều này, anh chàng an toàn phạm vi biến chìa khóa của mình.)


4

Những gì tôi khuyên là sử dụng một asserttuyên bố trong cuối cùng của bạn, theo một trong hai phong cách sau:

setCircle(circle, i, { current }) {
    if (i == current) {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
    } else if (i < current) {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
    } else {
        assert i > current
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    }
}

Hoặc một xác nhận mã chết:

setCircle(circle, i, { current }) {
    if (i == current) {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
    } else if (i < current) {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
    } else if (i > current) {
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    } else {
        assert False, "Unreachable code"
    }
}

Công cụ bao phủ mã thường có thể được cấu hình để bỏ qua mã như "khẳng định sai" khỏi báo cáo bảo hiểm.


Bằng cách đặt điều kiện trong một xác nhận, bạn ghi lại một cách rõ ràng tình trạng của một nhánh, nhưng không giống như một nhận xét, điều kiện xác nhận thực sự có thể được kiểm tra và sẽ thất bại nếu bạn giữ các xác nhận được kích hoạt trong quá trình phát triển hoặc sản xuất (tôi thường khuyên bạn nên giữ các xác nhận được kích hoạt trong sản xuất nếu chúng không ảnh hưởng đến hiệu suất quá nhiều).


1
Tôi không thích lựa chọn đầu tiên của bạn, thứ hai rõ ràng hơn nhiều rằng đó là một trường hợp không thể xảy ra.
Loren Pechtel

0

Tôi đã xác định một macro macro khẳng định điều tra đánh giá một điều kiện và trong bản dựng gỡ lỗi rơi vào trình gỡ lỗi.

Vì vậy, nếu tôi chắc chắn 100% rằng một trong ba điều kiện phải đúng, tôi viết

If condition 1 ...
Else if condition 2 .,,
Else if asserted (condition3) ...

Điều đó đủ rõ ràng rằng một điều kiện sẽ là đúng và không cần thêm chi nhánh cho một xác nhận.


-2

Tôi khuyên bạn nên tránh hoàn toàn khác . Sử dụng ifđể khai báo những gì khối mã được cho là xử lý và kết thúc khối bằng cách thoát khỏi hàm.

Điều này dẫn đến mã rất rõ ràng:

setCircle(circle, i, { current })
{
    if (i == current)
    {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
        return
    }
    if (i < current)
    {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
        return
    }
    if (i > current)
    {
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
        return
    }
    throw new Exception("Condition not handled.");
}

Trận chung kết iftất nhiên là dư thừa ... hôm nay. Nó có thể trở thành suy nghĩ rất quan trọng nếu / khi một số nhà phát triển trong tương lai sắp xếp lại các khối. Vì vậy, nó là hữu ích để lại nó trong đó.


1
Điều này thực sự rất không rõ ràng bởi vì bây giờ tôi cần xem xét liệu việc thực hiện nhánh đầu tiên có thể thay đổi kết quả của thử nghiệm thứ hai hay không. Cộng nhiều lợi nhuận vô nghĩa. Cộng với khả năng là không có điều kiện là đúng.
gnasher729

Tôi thực sự viết nếu những tuyên bố như thế này khi tôi cố tình mong đợi nhiều trường hợp có thể được thực hiện. Nó đặc biệt hữu ích trong các ngôn ngữ có câu lệnh chuyển đổi không cho phép rơi vào. Tôi nghĩ rằng nếu các lựa chọn là loại trừ lẫn nhau thì nên viết ra để rõ ràng là các trường hợp loại trừ lẫn nhau (sử dụng cách khác). Và nếu nó không được viết như vậy thì hàm ý là các trường hợp không loại trừ lẫn nhau (có thể thông qua).
Jerry Jeremiah

@ gnasher729 Tôi hoàn toàn hiểu phản ứng của bạn. Tôi biết một số người rất thông minh gặp rắc rối với "tránh người khác" và "trở về sớm" ... lúc đầu. Tôi khuyến khích bạn dành một chút thời gian để làm quen với ý tưởng và xem xét nó - bởi vì những ý tưởng này làm, một cách khách quan và đo lường, làm giảm sự phức tạp. Việc trả lại sớm thực sự giải quyết điểm đầu tiên của bạn (cần xem xét liệu một chi nhánh có thể thay đổi thử nghiệm khác hay không). Và "khả năng không có điều kiện là đúng" vẫn tồn tại bất kể; với phong cách này, bạn có được một ngoại lệ rõ ràng hơn là một lỗ hổng logic ẩn.
John Wu

Nhưng sự phức tạp theo chu kỳ của cả hai phong cách có giống nhau không? tức là bằng với số điều kiện
gaazkam
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.