2010-03-24 25 views
7

Hãy xem xét một tình huống mà bạn cần gọi các thường trình liên tiếp và dừng ngay sau khi một trả về một giá trị có thể được đánh giá là dương (true, object, 1, str (1)).Nó được coi là hình thức xấu để thực hiện một chức năng trong một tuyên bố có điều kiện?

Nó rất hấp dẫn để làm điều này:

if (fruit = getOrange()) 
elseif (fruit = getApple()) 
elseif (fruit = getMango()) 
else fruit = new Banana(); 

return fruit; 

tôi thích nó, nhưng đây không phải là một phong cách rất tái phát trong những gì có thể được coi mã sản xuất chuyên nghiệp. Một người có thể muốn xem mã phức tạp hơn như:

fruit = getOrange(); 
if(!fruit){ 
    fruit = getApple(); 
    if(!fruit){ 
     fruit = getMango(); 
     if(!fruit){ 
      fruit = new Banana(); 
     } 
    } 
} 

return fruit; 

Theo định kiến ​​về cấu trúc cơ bản, biểu mẫu trước có thể chấp nhận được không? Bạn muốn giới thiệu nó?

Edit:

Tôi xin lỗi cho những ai cho rằng các chức năng này đã có nghĩa là các nhà máy hoặc nhà xây dựng. Họ không phải, họ chỉ là người giữ chỗ. Câu hỏi đặt ra nhiều hơn về cú pháp so với "factorying". Các hàm này cũng có thể là lambda.

+1

Tùy thuộc vào ngôn ngữ. – SLaks

Trả lời

9

Nếu bạn muốn một cú pháp ngắn gọn, một số ngôn ngữ cho phép sử dụng "hợp lý hoặc" cho mục đích này (C# cung cấp một toán tử kết hợp rõ ràng, vì null không bị sai).

Python:

fruit = (getOrange() or 
      getApple() or 
      getMango() or 
      Banana()) 

C#:

fruit = getOrange() ?? 
     getApple() ?? 
     getMango() ?? 
     new Banana(); 
+0

Tôi tin rằng có những kế hoạch cho các phiên bản tương lai của PHP để làm cho phần thứ hai của các câu lệnh ternary kiểu C tùy chọn, cho phép bạn sử dụng '?:' Trong PHP giống như C# ''' '. –

+0

việc triển khai python sẽ phù hợp với javascript. các nhà điều hành coalescing sẽ là một bổ sung tốt đẹp cho nhiều ngôn ngữ. cám ơn vì cái này. –

1

Vấn đề, như tôi thấy, không phải là cấu trúc, mà là quy tắc lái xe. Tại sao getOrange lại đến trước getApple, vv?

Bạn đang có lẽ nhiều khả năng nhìn thấy một cái gì đó nhiều dữ liệu-driven:

enum FruitEnum 
{ 
    Orange, Apple, Mango, Banana 
} 

và riêng biệt,

List<FruitEnum> orderedFruit = getOrderedFruit(); 
int i = 0; 
FruitObj selectedFruit; 
while(selectedFruit == null && i <= orderedFruit.Count) 
{ 
    fruit = FruitFactory.Get(orderedFruit[i++]); 
} 
if(fruit == null) 
{ 
    throw new FruitNotFoundException(); 
} 

Điều đó nói rằng, để đơn giản hóa mã của bạn, bạn có thể sử dụng một nhà điều hành liên hiệp:

fruit = getOrange() ?? getApple() ?? getMango() ?? new Banana(); 
+0

Giống như enum, vì nó phù hợp với quan điểm thế giới hiện tại của OP. Nhà máy trái cây ... Chúa ơi. Có vẻ như rất nhiều rắc rối cho một túi trái cây. Nhưng tôi hiểu tại sao bạn làm theo cách đó. –

+2

Bạn đang giả định rất nhiều về các cấu trúc ngôn ngữ có sẵn mà OP không đưa vào câu hỏi của mình. – tvanfosson

+0

cũng có, không có lý do để giả sử ngôn ngữ của mình, hoặc bất kỳ khác, có điều hành coalesce. Tôi chỉ biết về C#, và đó chắc chắn không phải là C#, rất có thể đó là PHP. C# sẽ không cho phép gán bên trong nếu có điều kiện. – Tesserex

3

Trong ngôn ngữ được đánh máy mạnh không tương đương 0/null thành false và không 0/không null ue, tôi sẽ nói rằng nó có thể là an toàn, nhưng ít có thể đọc được trong trường hợp chung, nơi tên phương thức của bạn và số tham số có thể lớn hơn. Cá nhân tôi sẽ tránh nó, ngoại trừ một số thành ngữ tiêu chuẩn, trong trường hợp 0 ​​/ null tương đương với false và không 0/không null thành true chỉ vì nguy cơ gây nhầm lẫn với việc kiểm tra bình đẳng trong việc đọc mã. Một số thành ngữ trong ngôn ngữ một cách yếu ớt, đánh máy, giống như C, là sâu sắc đến nỗi nó không có ý nghĩa để tránh chúng, .e.g,

while ((line = getline()) != null) { 
    ... 
} 
4

tôi có thể nghĩ đến hai lựa chọn thay thế.

Điều đầu tiên chỉ cho phép bằng các ngôn ngữ như của bạn (PHP?), Trong đó đơn = trong điều kiện là ok.

if ((fruit = getOrange()) != null) 
    elseif ((fruit = getApple()) != null) 
    elseif ((fruit = getMango()) != null) 
    else fruit = new Banana(); 

Làm rõ rằng bạn đang so sánh và đơn = không phải là một sai lầm.

fruit = getOrange(); 
    if(!fruit) fruit = getApple(); 
    if(!fruit) fruit = getMango(); 
    if(!fruit) fruit = new Banana(); 

Giống như ví dụ thứ hai của bạn, nhưng loại bỏ việc làm tổ xấu xí hơn.

0

Để trả lời câu hỏi của bạn trực tiếp: nó là thường hình thức xấu có tác dụng phụ trong bản Tuyên Bố có điều kiện.

Là một công trình xung quanh, bạn có thể lưu trữ nhà xây dựng trái của bạn trong một mảng và tìm các nhà xây dựng đầu tiên mà trả không null (giả):

let constructors = [getOrange; getApple; getMango; fun() -> new Banana()] 
foreach constructor in constructors 
    let fruit = constructor() 
    if fruit != null 
     return fruit 

của nó giống như một nhà điều hành null-liên hiệp, nhưng tổng quát hơn . Trong C#, bạn có thể muốn sử dụng LINQ như sau:

var fruit = constructors 
    .Select(constructor => constructor()) 
    .Filter(x => x != null) 
    .First(); 

Ít nhất theo cách này bạn có thể vượt qua nhà thầu của bạn xung quanh như một lớp nhà máy, thay vì cứng mã hóa chúng với các nhà điều hành null-liên hiệp.

1

Trong C hoặc C++, bạn có thể viết:

return (fruit = getOrange()) ? fruit : 
     (fruit = getApple()) ? fruit : 
     (fruit = getMango()) ? fruit : 
     new Banana(); 

Lý do để tránh cả hai này và phiên bản đầu tiên của bạn không phải là "giáo điều về cấu trúc cơ bản", đó là nhiệm vụ của riêng mình trong một điều kiện được gây nhầm lẫn. Không phải tất cả các ngôn ngữ đều hỗ trợ nó, cho một điều. Đối với người khác, bạn có thể dễ dàng đọc sai là == hoặc người đọc có thể không chắc chắn liệu bạn có thực sự có ý nghĩa hay không hoặc có lẽ là dự định ==. Thêm != 0 vào mỗi điều kiện trở nên khá dày đặc và dài dòng.

GCC có một phần mở rộng cho phép:

return getOrange() ? : getApple() ? : getMango() ? : new Banana(); 

Điều tương tự cũng thường có thể đạt được với || hoặc or (nhưng không phải trong C hoặc C++).

Một khả năng khác là:

do { 
    fruit = getOrange(); 
    if (fruit) break; 
    fruit = getApple(); 
    if (fruit) break; 
    fruit = getMango(); 
    if (fruit) break; 
    fruit = new Banana(); 
} while (false); 

này thậm chí còn tốt hơn bằng một ngôn ngữ mà bạn có thể break ra khỏi một khối cơ bản mà bạn có thể với last trong Perl, vì bạn có thể bỏ qua những do/while(false). Nhưng có lẽ chỉ những lập trình viên lắp ráp mới thực sự thích nó.

0

Tôi không nghĩ rằng bất kỳ ai được đề cập đến là phiên bản đầu tiên có thể là một nỗi đau để bước qua trong trình gỡ lỗi. Nói chung, tôi tránh các bài tập và các cuộc gọi hàm trong điều kiện để giúp dễ dàng theo dõi thông qua thực thi, khi cần thiết (ngay cả khi tôi không cần gỡ lỗi nó, người khác có thể cần sửa đổi mã sau).

Các vấn đề liên quan