Là xác định một biến để đặt tên cho một đối số phương thức là một thực tiễn tốt?


73

Để dễ đọc, tôi thường thấy mình xác định các biến tạm thời trong khi gọi các hàm, chẳng hạn như đoạn mã sau

var preventUndo = true;
doSomething(preventUndo);

Phiên bản ngắn hơn của điều này sẽ là,

doSomething(true);

Nhưng khi tôi quay lại mã tôi thường tự hỏi những gì trueđề cập đến. Có một quy ước cho loại câu hỏi hóc búa này không?


5
Bạn có sử dụng một số loại IDE? Hầu hết sẽ có một số cách chỉ ra các tham số là gì đối với chức năng bạn đang gọi mà phần nào vô hiệu hóa sự cần thiết phải làm điều này.
Matthew Scharley

4
Tôi không sử dụng IDE, nhưng ngay cả khi đó tôi cho rằng gợi ý sẽ yêu cầu một số loại hành động (di chuột hoặc di chuyển đến dòng). Tôi muốn biết mã của tôi đang làm gì chỉ bằng cách nhìn vào nó.
phương pháp của

11
Nếu ngôn ngữ hỗ trợ, bạn có thể sử dụng một bảng liệt kê, chẳng hạn nhưdoSomething( Undo.PREVENT )
James P.

4
Nhưng bạn có thể định nghĩa Undo = { PREVENT = true, DONT_PREVENT = false }. Nhưng trong JavaScript, quy ước là làm như thế: function myFunction( mandatoryArg1, mandatoryArg2, otherArgs ) { /*...*/ }và sau đó myFunction( 1, 2, { option1: true, option2: false } ).
xavierm02

6
Nó chắc chắn phụ thuộc vào ngôn ngữ. Ví dụ, nếu đây là python, tôi sẽ đề nghị chỉ sử dụng các đối số từ khóa, ví dụ:doSomething(preventUndo=True)
Joel Cornett

Câu trả lời:


117

Giải thích các biến

Trường hợp của bạn là một ví dụ về giới thiệu giải thích tái cấu trúc biến . Nói tóm lại, một biến giải thích là một biến không thực sự cần thiết, nhưng cho phép bạn đặt tên rõ ràng cho một cái gì đó, với mục đích tăng khả năng đọc.

Mã chất lượng tốt truyền đạt ý định đến người đọc; và như một nhà phát triển chuyên nghiệp khả năng đọc và bảo trì là mục tiêu số 1 của bạn.

Như vậy, quy tắc ngón tay cái tôi muốn giới thiệu là: nếu mục đích của tham số của bạn không rõ ràng ngay lập tức, hãy sử dụng một biến để đặt cho nó một cái tên hay. Tôi nghĩ rằng đây là một thực hành tốt nói chung (trừ khi lạm dụng). Đây là một ví dụ nhanh chóng, dễ hiểu - xem xét:

editButton.Enabled = (_grid.SelectedRow != null && ((Person)_grid.SelectedRow).Status == PersonStatus.Active);

so với hơi dài hơn, nhưng rõ ràng hơn:

bool personIsSelected = (_grid.SelectedRow != null);
bool selectedPersonIsEditable = (personIsSelected && ((Person)_grid.SelectedRow).Status == PersonStatus.Active)
editButton.Enabled = (personIsSelected && selectedPersonIsEditable);

Thông số Boolean

Ví dụ của bạn thực sự nêu bật lý do tại sao các booleans trong API thường là một ý tưởng tồi - về phía gọi, họ không làm gì để giải thích những gì đang xảy ra. Xem xét:

ParseFolder(true, false);

Bạn sẽ phải tìm hiểu ý nghĩa của các tham số đó; nếu chúng là enum, nó sẽ rõ ràng hơn nhiều:

ParseFolder(ParseBehaviour.Recursive, CompatibilityOption.Strict);

Biên tập:

Đã thêm các tiêu đề và hoán đổi thứ tự của hai đoạn chính, vì có quá nhiều người đang tập trung vào phần tham số boolean (công bằng mà nói, đó đoạn đầu tiên ban đầu). Cũng đã thêm một ví dụ cho phần đầu tiên.


28
Trừ khi bạn sử dụng các tham số được đặt tên (đối số được đặt tên trong .NET Framework), trong trường hợp doSomething(preventUndo: true);này là đủ rõ ràng. Ngoài ra, tạo enum cho mọi boolean trong API của bạn? Nghiêm túc?
Arseni Mourzenko

12
@MainMa: Nếu ngôn ngữ bạn đang sử dụng không đủ để hỗ trợ các đối số được đặt tên, sử dụng enum là một lựa chọn tốt. Điều này không có nghĩa là bạn nên giới thiệu enum một cách có hệ thống ở mọi nơi.
Giorgio

18
@MainMa - Để trả lời câu hỏi đó, vấn đề thực sự là bạn không nên có các booleans trong API của mình (ít nhất là các công khai của bạn). Bạn cần thay đổi thành cú pháp đối số được đặt tên, được cho là không phải là thành ngữ (tại thời điểm viết) và trong mọi trường hợp, tham số boolean hầu như luôn biểu thị rằng phương thức của bạn thực hiện hai điều và bạn đang cho phép người gọi chọn. Nhưng quan điểm của câu trả lời của tôi là không có gì sai khi sử dụng giải thích các biến trong mã của bạn; đó thực sự là một thực hành rất tốt.
Daniel B

3
Nó là một phần vĩnh viễn của ngôn ngữ C #, nó cần thêm "sự chấp nhận chính thống" nào? Ngay cả đối với những người chưa biết về cấu trúc (có nghĩa là các nhà phát triển C # của bạn thậm chí chưa đọc tài liệu C #), thì nó khá rõ ràng về những gì nó làm.
Nate CK

3
Đó là lý do tại sao MS phát hành những bản tóm tắt hay của tất cả những thứ mới mà họ đã thêm vào trong mỗi phiên bản, tôi không nghĩ rằng nó sẽ tạo ra rất nhiều công việc để đọc một trong những điều này cứ sau vài năm: msdn.microsoft.com/en- chúng tôi / thư viện / bb383815% 28v = vs.100% 29.aspx
Nate CK

43

Đừng viết mã bạn không cần.

Nếu bạn thấy doSomething(true)khó hiểu, bạn nên thêm một bình luận:

// Do something and prevent the undo.
doSomething(true);

hoặc, nếu ngôn ngữ hỗ trợ nó, hãy thêm tên tham số:

doSomething(preventUndo: true);

Mặt khác, dựa vào IDE của bạn để cung cấp cho bạn chữ ký của phương thức được gọi:

nhập mô tả hình ảnh ở đây

Những trường hợp hữu ích

Đặt một biến bổ sung có thể hữu ích:

  1. Đối với mục đích gỡ lỗi:

    var productId = this.Data.GetLastProductId();
    this.Data.AddToCart(productId);
    

    Mã tương tự có thể được viết trong một dòng, nhưng nếu bạn muốn đặt một điểm dừng trước khi thêm sản phẩm vào giỏ hàng để xem id sản phẩm có chính xác hay không, viết hai dòng thay vì một dòng là một ý tưởng hay.

  2. Nếu bạn có một phương thức với nhiều tham số và mỗi tham số sẽ đi từ một đánh giá. Trong một dòng, điều này có thể trở nên hoàn toàn không thể đọc được.

    // Even with indentation, this is unreadable.
    var doSomething(
        isSomethingElse ? 0 : this.getAValue(),
        this.getAnotherOne() ?? this.default,
        (a + b + c + d + e) * f,
        this.hello ? this.world : (this.hello2 ? this.world2 : -1));
    
  3. Nếu việc đánh giá một tham số quá phức tạp. Một ví dụ về mã tôi đã thấy:

    // Wouldn't it be easier to have several if/else's (maybe even in a separate method)?
    do(something ? (hello ? world : -1) : (programmers ? stackexchange : (com ? -1 : 0)));
    

Tại sao không trong các trường hợp khác?

Tại sao bạn không nên tạo các biến bổ sung trong các trường hợp đơn giản?

  1. Không phải vì tác động hiệu suất. Đây sẽ là một giả định rất sai lầm của một nhà phát triển mới bắt đầu tối ưu hóa vi mô cho ứng dụng của mình. Không có tác động hiệu suất trong hầu hết các ngôn ngữ, vì trình biên dịch sẽ nội tuyến biến. Trong những ngôn ngữ mà trình biên dịch không làm điều đó, bạn có thể đạt được một vài micro giây bằng cách đặt nó bằng tay, điều này không đáng. Đừng làm vậy.

  2. Nhưng vì nguy cơ phân tách tên bạn đặt cho biến với tên của tham số.

    Thí dụ:

    Giả sử mã gốc là:

    void doSomething(bool preventUndo)
    {
        // Does something very interesting.
        undoHistory.removeLast();
    }
    
    // Later in code:
    var preventUndo = true;
    doSomething(preventUndo);
    

    Sau đó, một nhà phát triển làm việc trên các doSomethingthông báo rằng gần đây, lịch sử hoàn tác đã giới thiệu hai phương pháp mới:

    undoHistory.clearAll() { ... }
    undoHistory.disable() { ... }
    

    Bây giờ, preventUndodường như không rõ ràng lắm. Có nghĩa là chỉ hành động cuối cùng sẽ được ngăn chặn? Hoặc có lẽ điều đó có nghĩa là người dùng sẽ không thể sử dụng tính năng hoàn tác nữa? Hoặc lịch sử hoàn tác sẽ bị xóa? Tên rõ ràng hơn sẽ là:

    doSomething(bool hideLastUndo) { ... }
    doSomething(bool removeAllUndo) { ... }
    doSomething(bool disableUndoFeature) { ... }
    

    Vì vậy, bây giờ bạn có:

    void doSomething(bool hideLastUndo)
    {
        // Does something very interesting.
        undoHistory.removeLast();
    }
    
    // Later in code, very error prone, while the signature of the method is clear:
    var preventUndo = true;
    doSomething(preventUndo);
    

Thế còn enum?

Một số người khác đề nghị sử dụng enums. Trong khi nó giải quyết vấn đề trước mắt, nó tạo ra một vấn đề lớn hơn. Hãy xem:

enum undoPrevention
{
    keepInHistory,
    prevent,
}

void doSomething(undoPrevention preventUndo)
{
    doTheJob();
    if (preventUndo == undoPrevention.prevent)
    {
        this.undoHistory.discardLastEntry();
    }
}

doSomething(undoPrevention.prevent);

Vấn đề với điều đó:

  1. Đó là quá nhiều mã. if (preventUndo == undoPrevention.prevent)? Nghiêm túc?! Tôi không muốn viết như vậy ifmỗi lần.

  2. Thêm một yếu tố vào enum sẽ rất hấp dẫn sau này, nếu tôi đang sử dụng cùng một enum ở một nơi khác. Điều gì xảy ra nếu tôi sửa đổi nó như thế này:

    enum undoPrevention
    {
        keepInHistory,
        prevent,
        keepButDisable, // Keeps the entry in the history, but makes it disabled.
    }
    

    Chuyện gì xảy ra bây giờ? doSomethingPhương pháp sẽ làm việc như mong đợi? Để ngăn chặn điều này, nó sẽ yêu cầu viết phương pháp này ngay từ đầu:

    void doSomething(undoPrevention preventUndo)
    {
        if (![undoPrevention.keepInHistory, undoPrevention.prevent].contains(preventUndo))
        {
            throw new ArgumentException('preventUndo');
        }
    
        doTheJob();
        if (preventUndo == undoPrevention.prevent)
        {
            this.undoHistory.discardLastEntry();
        }
    }
    

    Các biến thể sử dụng booleans bắt đầu trông rất đẹp!

    void doSomething(bool preventUndo)
    {
        doTheJob();
        if (preventUndo)
        {
            this.undoHistory.discardLastEntry();
        }
    }
    

3
"Đó là quá nhiều mã. Nếu (ngănUndo == undoPrevent.prevent)? Nghiêm túc?! Tôi không muốn viết ifs như vậy mỗi lần." Ngoài ra, nếu bạn sử dụng boolean, bạn vẫn cần một câu lệnh if. Thành thật mà nói, những lời chỉ trích của bạn về điểm này có vẻ hơi giả tạo với tôi.
Giorgio

1
Tôi đang thiếu một giải pháp thay thế ở đây. Bạn có nói rằng anh ta nên bám vào câu nói không có gì là đúng và sai và dựa vào IDE (mà anh ta không sử dụng)? Một bình luận có thể thối đi như tên biến.
Bảo mật

2
@superM: Tôi không thấy việc thay đổi tên của một biến tạm thời chỉ được sử dụng cho cuộc gọi khó hơn nhiều. Nếu tên enum được thay đổi, thì nó thậm chí còn tốt hơn, bởi vì mã cuộc gọi sẽ không chạy nữa và ném lỗi vào mặt bạn. Nếu chức năng của chức năng được thay đổi hoàn toàn bởi một phần mở rộng enum, thì bạn phải xem lại tất cả các cuộc gọi để biết thêm tính chính xác, dù sao đi nữa, bạn sẽ lập trình theo hy vọng, không phải bằng logic. Thậm chí không phải suy nghĩ về việc thay đổi các giá trị, ví dụ như phủ nhận preventUndođể allowUndotrong chữ ký ...
Bảo vệ

4
@superM giữ bình luận đồng bộ với mã thực sự rất dễ gặp rắc rối, vì bạn không có sự trợ giúp nào từ trình biên dịch trong quá trình tìm kiếm sự không nhất quán của bạn.
Buhb

7
"... Dựa vào IDE của bạn để cung cấp cho bạn chữ ký của phương thức được gọi" - điều này hữu ích khi bạn viết mã, nhưng không nhiều khi bạn đọc nó. Khi tôi đọc mã cho một lớp, tôi muốn biết nó đang làm gì chỉ bằng cách nhìn vào nó. Nếu bạn phải dựa vào IDE để dễ đọc, mã của bạn không phải là tài liệu tự.
Phil

20

Không, bạn không nên viết phương thức với cờ boolean.

Để trích dẫn Robert C. Martin nói trong cuốn sách Clean Code của mình (ISBN-13 978-0-13-235088-4), "Chuyển một boolean vào một chức năng là một thực tế thực sự khủng khiếp."

Để diễn giải anh ta, lý do là việc bạn có một công tắc đúng / sai có nghĩa là phương pháp của bạn rất có thể làm hai việc khác nhau (nghĩa là "Làm gì đó với hoàn tác" và "Làm gì đó mà không hoàn tác"), và do đó nên chia thành hai phương thức khác nhau (có thể gọi bên trong cùng một thứ).

DoSomething()
{...

DoSomethingUndoable()
{....

7
Theo logic đó, hàm tạo HotDog duy nhất của tôi sẽ cần khoảng 16 phương thức khác nhau: HotDogWithMustardRelishKrautOnion (), HotDogWithMustardNorelishKrautOnion (), v.v. Hoặc, đó có thể là một HotDogWithOptions đơn giản (tùy chọn int), trong đó tôi diễn giải các tùy chọn dưới dạng một trường bit, nhưng sau đó chúng ta quay lại một phương thức thực hiện một số điều được cho là khác nhau. Vì vậy, nếu tôi đi với tùy chọn đầu tiên, tôi có phải viết một điều kiện lớn để sắp xếp hàm tạo nào sẽ gọi dựa trên những thứ có thể là các tham số Boolean không? Và nếu Q sử dụng một số nguyên, thì chữ A này không trả lời Q.
Caleb

4
Sau khi đọc tất cả các câu trả lời và nhìn vào mã của tôi, tôi đi đến kết luận rằng đây là cách làm đúng. doSomethingUndoable()có thể nắm bắt các thay đổi và sau đó gọidoSomething()
phương thức khởi động

1
Vì vậy, nếu tôi có một hàm xử lý nhiều và có tùy chọn gửi email khi hoàn thành, tôi không nên có một đối số boolean có thể ngăn email được gửi, tôi nên tạo một hàm riêng biệt?
yakatz

2
@Caleb Trong trường hợp này, tôi sẽ tạo một hotdog và thêm từng thành phần một, hoặc bằng ngôn ngữ được gõ mạnh, tôi sẽ chuyển một đối tượng HotDogSinstall trong đó tôi sẽ đặt tất cả các cờ boolean. Tôi sẽ không có 15 lá cờ đúng / sai khác nhau sẽ là một cơn ác mộng để duy trì. Hãy nhớ rằng mã là về bảo trì không viết vì đó là 80% chi phí của ứng dụng. var hd = MakeHotDog (); hd.Add (Hành tây); ...
Nick B.

2
@Caleb Tôi nghĩ rằng sự khác biệt là ví dụ của bạn sử dụng booleans như một trường dữ liệu thực tế, trong khi hầu hết việc sử dụng nó là để điều khiển luồng thực thi; do đó, lan can của Bob chống lại nó. Ngoài ra, lời khuyên của anh ấy là một chút về phía cực đoan - một vài dòng tiếp theo trong cuốn sách khuyên không nên có nhiều hơn 2 tham số, có thể là một lập trường thậm chí còn cực đoan hơn. Có phương pháp cho sự điên rồ mặc dù. Bạn hoàn toàn đúng khi nó không trả lời câu hỏi, một lỗi tôi đã tự trả lời.
Daniel B

5

Khi bạn nhìn vào hai lớp để xem chúng được ghép như thế nào, một trong các loạikhớp dữ liệu , trong đó đề cập đến mã trong một phương thức gọi lớp của một lớp khác và chỉ truyền dữ liệu, như tháng bạn muốn báo cáo và một loại khác thể loại là khớp nối điều khiển , gọi các phương thức và truyền vào một cái gì đó điều khiển hành vi của phương thức. Ví dụ tôi sử dụng trong lớp là verbosecờ hoặc reportTypecờ, nhưng preventUndocũng là một ví dụ tuyệt vời. Như bạn vừa trình bày, khớp nối điều khiển làm cho việc đọc mã gọi và hiểu những gì đang xảy ra thật khó khăn. Nó cũng làm cho mã cuộc gọi dễ bị tổn thương trước những thay đổi trong doSomething()đó sử dụng cùng một cờ để kiểm soát cả hoàn tác và lưu trữ hoặc thêm một tham số khác vàodoSomething() để kiểm soát việc lưu trữ, do đó phá mã của bạn, v.v.

Vấn đề là mã được ghép quá chặt chẽ. Vượt qua một bool để kiểm soát hành vi, theo tôi, là một dấu hiệu của một API xấu. Nếu bạn sở hữu API, hãy thay đổi nó. Hai phương pháp, doSomething()doSomethingWithUndo(), sẽ tốt hơn. nếu bạn không sở hữu nó, hãy tự viết hai phương thức trình bao bọc theo mã bạn sở hữu và yêu cầu một trong số chúng gọi doSomething(true)và cuộc gọi khác doSomething(false).


4

Nó không phải là vai trò của người gọi để xác định vai trò của các đối số. Tôi luôn dùng phiên bản nội tuyến và kiểm tra chữ ký của hàm được gọi nếu tôi có nghi ngờ.

Biến nên được đặt tên theo vai trò của chúng trong phạm vi hiện tại. Không phải phạm vi mà chúng được gửi.


1
vai trò của biến trong phạm vi hiện tại là cho biết nó được sử dụng để làm gì. Tôi không thấy điều này mâu thuẫn với việc sử dụng các biến tạm thời của OP.
superM

4

Những gì tôi sẽ làm trong JavaScript là hàm có một đối tượng làm tham số duy nhất giống như thế này:

function doSomething(settings) {
    var preventUndo = settings.hasOwnProperty('preventUndo') ? settings.preventUndo : false;
    // Deal with preventUndo in the normal way.
}

Sau đó gọi nó bằng:

doSomething({preventUndo: true});

HẠ !!! Cả hai chúng tôi đã thực hiện một doSomething(x)phương pháp! lol ... tôi thậm chí không nhận thấy bài viết của bạn cho đến bây giờ.
thịt

Điều gì sẽ xảy ra nếu tôi chuyển một chuỗi "noUndo" và làm thế nào rõ ràng cho người sử dụng chữ ký của bạn những cài đặt nào được cho là chứa mà không xem xét việc triển khai?
Nick B.

1
Tài liệu mã của bạn. Đó là những gì mọi người khác làm. Nó làm cho việc đọc mã của bạn dễ dàng hơn, viết nó chỉ phải xảy ra một lần.
Ridecar2

1
@NickB. quy ước là mạnh mẽ. Nếu hầu hết mã của bạn chấp nhận các đối tượng có đối số thì điều này là rõ ràng.
orip

4

Tôi thích tận dụng các tính năng ngôn ngữ để giúp bản thân rõ ràng hơn. Ví dụ trong C #, bạn có thể chỉ định tham số theo tên:

 CallSomething(name: "So and so", age: 12, description: "A random person.");

Trong JavaScript, tôi thường thích sử dụng một đối tượng tùy chọn làm đối số cho lý do này:

function doSomething(args) { /*...*/ }

doSomething({ name: 'So and so', age: 12, description: 'A random person.' });

Đó chỉ là sở thích của tôi. Tôi cho rằng nó sẽ phụ thuộc rất nhiều vào phương thức được gọi và cách IDE giúp nhà phát triển hiểu chữ ký.


1
Đó là "nhược điểm" đối với các ngôn ngữ được đánh máy lỏng lẻo. Bạn thực sự không thể thực thi chữ ký mà không có một số xác nhận. Tôi không biết bạn có thể thêm tên như thế trong JS.
James P.

1
@JamesPoulson: Có lẽ bạn đã biết bạn có thể làm điều đó trong JS và chỉ không nhận ra điều đó. Tất cả đã kết thúc trong JQuery: $.ajax({url:'', data:''})v.v.
xác thịt

Thật. Thoạt nhìn có vẻ khác thường nhưng nó tương tự như các đối tượng giả nội tuyến có thể tồn tại trong một số ngôn ngữ.
James P.

3

Tôi thấy đây là cách đơn giản và dễ đọc nhất:

enum Undo { ALLOW, PREVENT }

doSomething(Undo u) {
    if (Undo.ALLOW == u) {
        // save stuff to undo later.
    }
    // do your thing
}

doSomething(Undo.ALLOW);

MainMa không thích điều đó. Chúng tôi có thể phải đồng ý không đồng ý hoặc chúng tôi có thể sử dụng giải pháp mà Joshua Bloch đề xuất:

enum Undo {
    ALLOW {
        @Override
        public void createUndoBuffer() {
            // put undo code here
        }
    },
    PREVENT {
        @Override
        public void createUndoBuffer() {
            // do nothing
        }
    };

    public abstract void createUndoBuffer();
}

doSomething(Undo u) {
    u.createUndoBuffer();
    // do your thing
}

Bây giờ nếu bạn đã từng thêm một Undo.LIMITED_UNDO, mã của bạn sẽ không biên dịch trừ khi bạn triển khai phương thức createUndoBuffer (). Và trong doS Something () không có if (Undo.ALLOW == u). Tôi đã thực hiện cả hai cách và phương pháp thứ hai khá nặng nề và hơi khó hiểu khi Undo enum mở rộng sang các trang và trang mã, nhưng nó khiến bạn phải suy nghĩ. Tôi thường gắn bó với phương pháp đơn giản hơn để thay thế một boolean đơn giản bằng enum 2 giá trị cho đến khi tôi có lý do để thay đổi. Khi tôi thêm một giá trị thứ ba, tôi sử dụng IDE của mình để "Tìm kiếm sử dụng" và sau đó sửa mọi thứ.


2

Tôi nghĩ rằng giải pháp của bạn làm cho mã của bạn dễ đọc hơn một chút nhưng tôi sẽ tránh việc xác định một biến phụ chỉ để làm cho hàm của bạn gọi rõ ràng hơn. Ngoài ra, nếu bạn muốn sử dụng một biến phụ, tôi sẽ đánh dấu nó là hằng số nếu ngôn ngữ lập trình của bạn hỗ trợ nó.

Tôi có thể nghĩ về hai lựa chọn thay thế không liên quan đến một biến phụ:

1. Sử dụng một nhận xét thêm

doSomething(/* preventUndo */ true);

2. Sử dụng enum hai giá trị thay vì boolean

enum Options
{
    PreventUndo,
    ...
}

...

doSomething(PreventUndo);

Bạn có thể sử dụng thay thế 2 nếu ngôn ngữ của bạn hỗ trợ enums.

BIÊN TẬP

Tất nhiên, sử dụng các đối số được đặt tên cũng là một tùy chọn, nếu ngôn ngữ của bạn hỗ trợ chúng.

Về mã hóa thêm cần thiết cho enums, nó thực sự có vẻ không đáng kể đối với tôi. Thay vì

if (preventUndo)
{
    ...
}

bạn có

if (undoOption == PreventUndo)
{
    ...
}

hoặc là

switch (undoOption)
{
  case PreventUndo:
  ...
}

Và ngay cả khi nó gõ nhiều hơn một chút, hãy nhớ rằng mã được viết một lần và đọc nhiều lần, vì vậy nó có thể đáng để gõ thêm một chút bây giờ để tìm mã dễ đọc hơn sáu tháng sau.


2

Tôi đồng ý với những gì @GlenPeterson nói:

doSomething(Undo.ALLOW); // call using a self evident enum

nhưng cũng insterest sẽ là như sau vì dường như chỉ có hai tính tích cực (đúng hoặc sai)

//Two methods    

doSomething()  // this method does something but doesn't prevent undo

doSomethingPreventUndo() // this method does something and prevents undo

2
Ý tưởng hay - tôi đã không nghĩ về điều đó. Khi bạn gặp nhiều phức tạp hơn như doS Something (String, String, String, boolean, boolean, boolean) thì các hằng số được đặt tên là giải pháp hợp lý duy nhất. Chà, Josh Bloch gợi ý một đối tượng nhà máy và nhà xây dựng như trong: MyT BreathMaker thingMaker = MyT BreathMaker.new (); thingMaker.setFirstString ("Hi"); thingMaker.setSecondString ("Có"); v.v ... Điều t = thingMaker.makeIt (); t.doSthing (); Đó chỉ là công việc nhiều hơn, vì vậy nó đáng giá hơn.
GlenPeterson

Một vấn đề với cách tiếp cận thứ hai là nó gây khó khăn khi viết một phương thức sử dụng doSomethingnhưng cho phép người gọi lựa chọn có cho phép hoàn tác hay không. Một biện pháp khắc phục có thể là quá tải trong đó có tùy chọn Boolean, nhưng cũng có các phiên bản trình bao bọc gọi phiên bản cũ với tham số luôn luôn đúng hoặc luôn luôn sai.
supercat

@GlenPeterson Một nhà máy là một khả năng và có thể có trong Javascript (được đề cập bởi OP).
James P.

2

Vì bạn chủ yếu hỏi về javascript, bằng cách sử dụng coffeescript, bạn có thể có một đối số được đặt tên như cú pháp, siêu dễ:

# declare method, ={} declares a default value to prevent null reference errors
doSomething = ({preventUndo} = {}) ->
  if preventUndo
    undo = no
  else
    undo = yes

#call method
doSomething preventUndo: yes
#or 
doSomething(preventUndo: yes)

biên dịch thành

var doSomething;

doSomething = function(_arg) {
  var preventUndo, undo;
  preventUndo = (_arg != null ? _arg : {}).preventUndo;
  if (preventUndo) {
    return undo = false;
  } else {
    return undo = true;
  }
};

doSomething({
  preventUndo: true
});

doSomething({
  preventUndo: true
});

Hãy vui vẻ tại http://js2coffee.org/ để thử các khả năng


Tôi chỉ sử dụng coffeeScript khi tôi cần tránh sự điên rồ OOP của javascript. Đây là một giải pháp tốt mà tôi có thể sử dụng khi mã hóa một mình, thật khó để biện minh cho đồng nghiệp rằng tôi đang truyền một đối tượng thay vì boolean vì mục đích dễ đọc!
phương pháp của

1

Chỉ dành cho một giải pháp ít thông thường hơn và vì OP đã đề cập đến Javascript, một khả năng là sử dụng một mảng kết hợp để ánh xạ các giá trị. Ưu điểm so với một boolean duy nhất là bạn có thể có nhiều đối số như bạn muốn và không phải lo lắng về trình tự của chúng.

var doSomethingOptions = new Array();

doSomethingOptions["PREVENTUNDO"] = true;
doSomethingOptions["TESTMODE"] = false;

doSomething( doSomethingOptions );

// ...

function doSomething( doSomethingOptions ){
    // Check for null here
    if( doSomethingOptions["PREVENTUNDO"] ) // ...
}

Tôi nhận ra đây là một phản xạ của một người nào đó từ một nền tảng được đánh máy mạnh mẽ và có thể không thực tế lắm nhưng hãy xem xét điều này cho độc đáo.

Một khả năng khác là có một đối tượng và cũng có thể có trong Javascript. Tôi không chắc chắn 100% điều này là chính xác. Có một cái nhìn vào các mẫu như Nhà máy để có thêm cảm hứng.

function SomethingDoer( preventUndo ) {
    this.preventUndo = preventUndo;
    this.doSomething = function() {
            if( this.preventUndo ){
                    // ...
            }
    };
}

mySomethingDoer = new SomethingDoer(true).doSomething();

1
Tôi nghĩ rằng điều này có thể được viết tốt hơn trong ký hiệu dấu chấm ( doSomethingOptions.PREVENTUNDO) thay vì ký hiệu truy cập mảng.
Paŭlo Ebermann

1

Trọng tâm của câu hỏi của bạn và của các câu trả lời khác là cải thiện khả năng đọc nơi hàm được gọi, đây là trọng tâm tôi đồng ý. Bất kỳ hướng dẫn cụ thể nào, sự kiện "không có đối số boolean", sẽ luôn luôn hoạt động như là phương tiện cho mục đích này và không trở thành và tự kết thúc.

Tôi nghĩ thật hữu ích khi lưu ý rằng vấn đề này chủ yếu được khắc phục khi ngôn ngữ lập trình hỗ trợ các đối số / đối số từ khóa có tên, như trong C # 4 và Python hoặc trong đó các đối số phương thức được xen kẽ trong tên phương thức, như trong Smalltalk hoặc Objective-C.

Ví dụ:

// C# 4
foo.doSomething(preventUndo: true);
# Python
foo.doSomething(preventUndo=True)
// Objective-C
[foo doSomethingWith:bar shouldPreventUndo:YES];

0

Người ta nên tránh truyền các biến boolean làm đối số cho các hàm. Bởi vì các chức năng nên làm một việc tại một thời điểm. Bằng cách chuyển biến boolean, hàm có hai hành vi. Điều này cũng tạo ra vấn đề dễ đọc cho giai đoạn sau hoặc cho các lập trình viên khác có xu hướng xem mã của bạn. Điều này cũng tạo ra vấn đề trong việc kiểm tra chức năng. Có lẽ trong trường hợp này bạn phải tạo hai trường hợp thử nghiệm. Hãy tưởng tượng nếu bạn có một hàm có câu lệnh chuyển đổi và thay đổi hành vi của nó dựa trên loại chuyển đổi thì bạn phải có rất nhiều trường hợp thử nghiệm khác nhau được xác định cho nó.

Bất cứ khi nào người ta bắt gặp có một đối số boolean cho hàm, họ phải điều chỉnh mã sao cho họ không viết đối số boolean nào cả.


Vì vậy, bạn sẽ chuyển đổi một hàm với một đối số boolean trong hai hàm chỉ khác nhau trong một phần nhỏ của mã của chúng (trong đó bản gốc sẽ có một if(param) {...} else {...})?
Paŭlo Ebermann

-1
int preventUndo = true;
doSomething(preventUndo);

Một trình biên dịch tốt cho các ngôn ngữ thấp như C ++ sẽ tối ưu hóa mã máy của 2 dòng trên như dưới đây:

doSomething(true); 

vì vậy nó không quan trọng trong hiệu suất nhưng sẽ tăng khả năng đọc.

Ngoài ra, rất hữu ích khi sử dụng một biến trong trường hợp nó sẽ trở thành biến sau đó và cũng có thể chấp nhận các giá trị khác.

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.