Có cần thiết phải thêm trường hợp mặc định trong khi sử dụng trường hợp chuyển đổi?


41

Trong một lần xem xét mã gần đây, tôi đã được yêu cầu đặt defaultcác trường hợp vào tất cả các tệp ở bất kỳ nơi nào switchđược sử dụng, ngay cả khi không có gì để làm default. Điều đó có nghĩa là tôi phải đặt defaulttrường hợp và không viết gì trong đó.

Đây có phải là điều đúng để làm gì? Mục đích gì nó sẽ phục vụ?


9
Tùy thuộc vào ... Ý tôi là phong cách mã của người giám sát.
SD

1
Tôi nghĩ thậm chí có những trường hợp bạn không muốn một trường hợp mặc định. Xem xét chuyển đổi qua một enum. Công tắc của bạn xác định một trường hợp cho tất cả các giá trị có thể. Bây giờ việc thêm một giá trị vào một enum sẽ dẫn đến việc thêm một trường hợp trong chuyển đổi đó. Nếu bạn không làm như vậy, đây có thể là một lỗi, vì bạn muốn làm gì đó cho giá trị enum mới này, nhưng mã của bạn thì không. Trình biên dịch của bạn có thể cảnh báo bạn (tôi nghĩ rằng, không chắc chắn về Java) về điều đó, nhưng chỉ khi bạn không đặt trường hợp mặc định. Ngoài ra, bạn có thể in cảnh báo trong trường hợp mặc định khi bạn chắc chắn mã không bao giờ đến được.
leemes

6
Tôi và bạn bè gọi đây là trường hợp "Ngoại lệ dành cho nhà phát triển" - khi điều gì đó trong mã xảy ra mà bạn biết sẽ không bao giờ xảy ra (vì lý do logic / mã), sau đó chúng tôi thêm "Ngoại lệ dành cho nhà phát triển". Theo cách đó, nếu điều này từng xảy ra, Ngoại lệ dành cho nhà phát triển sẽ bị ném và ít nhất chúng ta biết mọi thứ đã sai nghiêm trọng ở đâu.
Marco

Câu trả lời:


31

Có vẻ như có ba trường hợp khi một defaulttuyên bố là không cần thiết:

  1. không có trường hợp nào còn lại, vì có một bộ giá trị giới hạn nhập vào switch case. Nhưng điều này có thể thay đổi theo thời gian (cố ý hoặc vô tình) và sẽ tốt hơn default casenếu có bất cứ điều gì thay đổi _ bạn có thể đăng nhập hoặc cảnh báo người dùng về một giá trị sai.

  2. bạn biết làm thế nào và ở đâu switch casesẽ được sử dụng và những giá trị nào sẽ nhập vào nó. Một lần nữa, điều này có thể thay đổi và có thể cần xử lý thêm.

  3. các trường hợp khác không cần bất kỳ xử lý đặc biệt. Nếu đây là trường hợp, tôi nghĩ bạn được yêu cầu thêm một default case, bởi vì đó là một kiểu mã hóa được chấp nhận và nó làm cho mã của bạn dễ đọc hơn.

Hai trường hợp đầu tiên dựa trên các giả định. Vì vậy (giả sử bạn làm việc trong nhóm không quá nhỏ vì bạn có đánh giá mã thường xuyên), bạn không thể đủ khả năng đưa ra các giả định đó. Bạn không biết ai sẽ làm việc với mã của bạn hoặc thực hiện cuộc gọi đến các hàm / gọi các phương thức trong mã của bạn. Tương tự, bạn có thể cần phải làm việc với mã của người khác. Có cùng kiểu mã hóa sẽ giúp bạn dễ dàng xử lý mã của ai đó (bao gồm cả mã của bạn).


14
Trong trường hợp 1 và 2, một trường hợp mặc định sẽ là một lỗi. Nếu kiểu mã hóa của bạn luôn sử dụng mặc định, thì hãy làm điều đó, nhưng làm cho nó phát ra lỗi trong 1 và 2. Có thể, nó sẽ phát ra nó vào thời gian biên dịch, nhưng điều đó có thể không phải lúc nào cũng có thể, nếu có trong java. Ít nhất tôi nghĩ rằng điều quan trọng đối với người dùng là nhận được thông báo rằng "Bạn đang tự bắn vào chân mình bằng cách chuyển qua giá trị này"
martiert

11
Một trường hợp mặc định luôn là một ý tưởng tốt, bởi vì, ví dụ trong trường hợp của một enum, nếu ai đó thêm một mục mới vào enum, trường hợp mặc định của bạn sẽ làm một cái gì đó tương tự như throw new IllegalStateException("Unrecognised case "+myEnum)cho bạn biết lỗi ở đâu và lỗi gì.
Giàu

Điều này có nghĩa là bạn phải luôn có một elsemệnh đề sau mỗi lần if/else ifngay cả khi bạn biết điều đó không nên xảy ra? Có vẻ như đó không phải là một ý tưởng tốt đối với tôi ...
jadkik94

2
Nếu tôi có thể thêm một giai thoại từ công việc của mình: tôi đã có một phương thức xuất xưởng sử dụng một enum để khởi tạo một số lớp. Tôi đã để lại hướng dẫn rằng bất cứ khi nào bạn thêm một lớp mới vào dự án đó, bạn phải thêm một giá trị cho enum và trường hợp o chuyển đổi. Tất nhiên, một tuần sau khi tôi đóng mã, nó ngừng hoạt động, với một trong những ngoại lệ không bao giờ được nêu ra. Tôi đi đến lập trình viên và hỏi anh ta: "bạn đã thêm một trường hợp vào công tắc chưa?" Và chắc chắn, anh ấy đã không làm thế. Vào ngày đó tôi đã thay đổi ngoại lệ thành "nếu bạn nhận được thông báo này, hãy đổ lỗi cho nhà phát triển cuối cùng đã chạm vào mã".
Ziv

28

Đây có phải là thiong phải làm? Mục đích gì nó sẽ phục vụ?

Không có gì lạ khi các tiêu chuẩn mã hóa của công ty yêu cầu một trường hợp mặc định cho tất cả các switchcâu lệnh. Một lý do cho nó là nó giúp người đọc dễ dàng tìm thấy sự kết thúc của switch. Một lý do khác, có lẽ tốt hơn là nó khiến bạn suy nghĩ một chút về việc mã của bạn nên làm gì khi điều kiện không phù hợp với mong đợi của bạn. Bất kể lý do cho yêu cầu là gì, nếu đó là tiêu chuẩn của công ty, bạn nên tuân theo yêu cầu đó trừ khi không có lý do kín đáo.

Nếu bạn tin rằng switchbao gồm các trường hợp của bạn cho mọi điều kiện có thể, thì một điều tốt để làm là đặt một asserttuyên bố trong trường hợp mặc định. Theo cách đó, khi ai đó thay đổi mã và vô tình thêm một điều kiện mà bạn switchkhông bao gồm, họ sẽ nhấn assertvà nhận ra rằng họ cần giải quyết phần đó của mã.

Nếu bạn switchchỉ bao gồm một vài điều kiện có thể, nhưng không có gì đặc biệt phải làm cho những điều kiện khác, bạn có thể để trống trường hợp mặc định. Đó là một ý tưởng tốt để thêm một nhận xét trong trường hợp đó để chỉ ra rằng trường hợp mặc định là cố ý để trống vì các điều kiện xảy ra không cần phải thực hiện bất kỳ công việc nào.


1
assert
FLY

1
@FLY Đó là một từ khóa trong Java và thường là macro trong C, C ++, v.v. Dù sao đi nữa, một khẳng định được sử dụng để kiểm tra rằng một số giả định vẫn đúng và ném và ngoại lệ hoặc gây ra lỗi nếu không.
Caleb

Tôi đã chỉnh sửa nhận xét trước đây của mình bằng một liên kết đến wikipedia
FLY

Bạn không nên để một trường hợp trống rỗng. Nếu công tắc của bạn không bao gồm tất cả các điều kiện có thể, bạn nên xác nhận các điều kiện khác trong trường hợp mặc định của mình với cùng lý do bạn đề xuất để thêm một xác nhận trong trường hợp mặc định trong đoạn thứ hai của bạn.
rold2007

12

Nếu bạn "chuyển đổi" sang loại liệt kê thuần túy, sẽ rất nguy hiểm nếu có dự phòng mặc định. Khi bạn thêm các giá trị vào loại liệt kê, trình biên dịch sẽ làm nổi bật các công tắc với các giá trị mới không được bảo hiểm. Nếu bạn có mệnh đề mặc định ở đó, trình biên dịch sẽ giữ im lặng và bạn có thể bỏ lỡ nó.


1
+1 để nắm bắt vấn đề tại thời gian biên dịch và không phải lúc chạy.
SylvainD

Tôi tự hỏi làm thế nào hoàn toàn sai lầm câu trả lời được bình chọn và chấp nhận. Googling nhanh cho thấy trình biên dịch java có loại cảnh báo như vậy. Tại sao trên trái đất bạn sẽ chờ lỗi trong thời gian chạy, folks?
Artur

Tôi không biết về hành vi này. My mẫu mã ideone ideone.com/WvytWq gợi ý khác. Giả sử bạn nói là đúng, điều gì xảy ra nếu ai đó thêm giá trị vào loại liệt kê, nhưng bạn không biên dịch lại mã của mình?
emory


1
Rõ ràng, các giá trị mới chỉ không được xử lý bởi câu lệnh switch mà không có trường hợp mặc định. Nhưng nếu bạn không biên dịch lại các triển khai của mình khi giao diện thay đổi, hệ thống xây dựng của bạn thực sự bị hỏng.
Artur

9

Trong nhiều khía cạnh, câu hỏi này giống như câu hỏi Tôi thường cần một elsemệnh đề ở cuối if/ else ifthang bao gồm mọi tùy chọn .

Câu trả lời là, về mặt cú pháp, không có bạn. Nhưng có một ... tuy nhiên ...

Một defaultmệnh đề có thể ở đó vì (ít nhất) hai lý do:

  1. Là một người xử lý lỗi - chắc chắn, không bao giờ nên đến đây! là một yêu cầu có thể được thực hiện, nhưng nếu nó xảy ra thì sao? Tham nhũng dữ liệu, hoặc thậm chí tệ hơn, không có xác thực dữ liệu là các tuyến dẫn đến lỗi chương trình nếu không được đặt đúng cách. Trong trường hợp này, nó không nên là một mệnh đề trống!
  2. Thiết kế / Bảo hiểm mã - ngay cả ở dạng đơn giản nhất của biểu đồ dòng chảy, có hai tuyến từ một câu lệnh if và luôn luôn là otherwisetừ một trường hợp. Không có lý do gì để không bao gồm những thứ này trong mã nguồn.

Triết lý của tôi luôn khá đơn giản - đánh giá trường hợp xấu nhất trong hai lựa chọn và đi cho an toàn nhất. Trong trường hợp trống elsehoặc defaultmệnh đề, các tùy chọn trường hợp xấu nhất là:

  • Bao gồm chúng: Thêm ba hoặc bốn dòng mã "dự phòng".
  • Không bao gồm chúng: Dữ liệu giả hoặc một điều kiện bất ngờ không bị mắc kẹt, có khả năng gây ra lỗi chương trình.

Quá sức? Có lẽ ... nhưng sau đó phần mềm của tôi có khả năng giết người nếu nó sai. Tôi thà không chấp nhận rủi ro đó.


Bên cạnh đó, các hướng dẫn của MISRA-C {xem hồ sơ để liên kết} đề xuất một defaultđiều khoản cho mọiswitch


Không chỉ ở cuối một cái thang. Câu hỏi đặt ra là: Tôi cần một elsesau if. Nhưng như bạn đã nói, không, không phải vậy.
ott--

Là một trình xử lý lỗi, tôi đề xuất một hình thức sửa đổi một chút để tránh mặc định trống. Đối với việc giết người, tôi không nghĩ nó thực sự quan trọng - mọi người sẽ chết như thế dù bạn có điều khoản mặc định hay không, nhưng nó sẽ giúp bạn dễ dàng hiểu được tại sao họ chết.
emory

Điểm hay, @emory - điều đó không rõ ràng lắm, phải không ... đã được chỉnh sửa :)
Andrew

6

Java không bắt buộc bạn phải có một câu lệnh 'mặc định' nhưng đó là một cách tốt để có tất cả thời gian, ngay cả khi mã có thể không bao giờ đạt được (ngay bây giờ). Đây là một vài lý do:

Bằng cách có một mệnh đề mặc định không thể truy cập, bạn hiển thị cho người đọc mã của bạn rằng bạn đã xem xét các giá trị và biết bạn đang làm gì. Bạn cũng cho phép thay đổi trong tương lai, ví dụ: một giá trị enum mới được thêm vào, công tắc không nên âm thầm bỏ qua giá trị mới; thay vào đó bạn có thể ném Ngoại lệ hoặc làm việc khác.

Để bắt một giá trị không mong muốn (trong trường hợp bạn không bật enum) được truyền vào - chẳng hạn, nó có thể lớn hơn hoặc ít hơn so với những gì bạn mong đợi, chẳng hạn.

Để xử lý các hành động 'mặc định' - trong đó các công tắc dành cho hành vi đặc biệt. Ví dụ, một biến có thể được khai báo bên ngoài công tắc nhưng không được khởi tạo và mỗi trường hợp khởi tạo nó thành một cái gì đó khác nhau. Mặc định trong trường hợp này có thể khởi tạo nó thành một giá trị mặc định để mã ngay sau khi chuyển đổi không bị lỗi / ném ngoại lệ.

Ngay cả khi bạn quyết định không đặt gì vào mặc định (không có ngoại lệ, ghi nhật ký, v.v.) thì ngay cả một nhận xét để nói rằng bạn đã xem mặc định thực tế sẽ không bao giờ xảy ra có thể giúp đọc mã của bạn; nhưng điều đó đi xuống sở thích cá nhân.


2

Tôi cũng sẽ nói thêm rằng nó phụ thuộc vào triết lý của ngôn ngữ bạn đang sử dụng. Ví dụ, trong Erlang, đó là cách tiếp cận phổ biến để 'để nó bị sập', bạn sẽ không xác định trường hợp mặc định mà hãy để casecâu lệnh của bạn gây ra lỗi nếu một tình huống xảy ra không được phục vụ.


0

Bạn phải luôn luôn có một mặc định trừ khi bạn có thể bao quát tất cả các trường hợp. Không có chi phí gì, và đó là bảo hiểm chống lại việc không duy trì tuyên bố chuyển đổi của bạn trong một chương trình phát triển.

Nếu bạn thực sự chắc chắn rằng bạn không quan tâm đến bất kỳ trường hợp nào khác, thì mặc định: break; // không quan tâm nhưng mặc định mặc định sẽ là một cái gì đó như mặc định: ném Lỗi mới ("giá trị không mong muốn");

Tương tự như vậy, bạn không bao giờ nên "bỏ qua" một cấu trúc trường hợp không cần thiết mà không thêm nhận xét vào hiệu ứng mà bạn dự định bỏ qua. Java đã sai điều này ở giai đoạn thiết kế.


-2

Xem xét

enum Gender
{
       MALE ,
       FEMALE ;
}

void run ( Gender g )
{
      switch ( g )
      {
           case MALE :
                 // code related to males
                 break ;
           default :
                 assert Gender . FEMALE . equals ( g ) ;
                 // code related to females
                 break ;
      }
}

Tôi sẽ lập luận rằng điều này là vượt trội so với trường hợp MALE, trường hợp FEMALE và trường hợp mặc định đưa ra một ngoại lệ.

Trong giai đoạn thử nghiệm của bạn, máy tính sẽ kiểm tra xem g có phải là FEMALE không. Trong quá trình sản xuất, kiểm tra sẽ được bỏ qua. (Vâng, một vi mô hóa!)

Quan trọng hơn, bạn sẽ duy trì mục tiêu bảo hiểm mã 100%. Nếu bạn đã viết một trường hợp mặc định ném Ngoại lệ, bạn sẽ kiểm tra nó như thế nào?


1
1. Không cần tối ưu hóa vi mô - loại bỏ mã chết được sử dụng trong trình biên dịch trong 30 năm qua hoặc lâu hơn và nó sẽ bị loại bỏ bởi JIT. 2. Bảo hiểm mã 100% thường không được coi là quan trọng (một mặt bảo hiểm 100% có thể không bắt được tất cả các trường hợp cạnh, trên một thử nghiệm khác sẽ không bắt được các dòng assert_not_reached trên chương trình chính xác). Trong trường hợp này không để lại trường hợp mặc định sẽ gây ra lỗi trình biên dịch. Mặt khác - bạn sẽ kiểm tra xem xác nhận có hoạt động như thế nào không (bạn thay đổi vấn đề và che giấu nó bằng phạm vi bảo hiểm 100% thay vì có cùng một dòng 'nó sẽ không xảy ra, tôi hứa').
Maciej Piechotka

1
3. Khi nào Gender . FEMALE . equals ( FEMALE ) ;đánh giá false? Bạn có nghĩa là Gender.FEMALE.equals (g)nhưng vì bạn có thể phụ thuộc vào tài liệu tham khảo để enums ổn định, bạn chỉ có thể viết g == Gender.FEMALE. 4. (Ý kiến ​​cá nhân) mã như vậy khó đọc hơn nhiều và sẽ tạo ra các lỗi khó hiểu hơn một chút.
Maciej Piechotka

@MaciejPiechotka bắt tốt về g. Đúng, microoptimization là một nonbenefit, nhưng nó cũng không phải là nhược điểm. Bảo hiểm mã 100% là một lợi ích nếu đó là một trong những mục tiêu của bạn và công cụ bảo hiểm mã của bạn không kiểm tra các xác nhận (điều này không nên).
emory

Vậy thì tại sao nó lại là một trong những mục tiêu của tôi? Phần 'thú vị' nên được kiểm tra cho tất cả các trường hợp cạnh (bất kể chúng có ánh xạ tới các dòng mã không) và các phần 'nhàm chán' có thể được bỏ qua để tiết kiệm thời gian kiểm tra. Bảo hiểm mã IMHO là số liệu xấu của các bài kiểm tra.
Maciej Piechotka
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.