mã hợp đồng / khẳng định: những gì với kiểm tra trùng lặp?


10

Tôi là một fan hâm mộ lớn của việc viết các xác nhận, hợp đồng hoặc bất kỳ loại séc nào có sẵn bằng ngôn ngữ tôi đang sử dụng. Một điều làm phiền tôi một chút là tôi không chắc thực tiễn chung là gì để xử lý các kiểm tra trùng lặp.

Ví dụ tình huống: Trước tiên tôi viết hàm sau

void DoSomething( object obj )
{
  Contract.Requires<ArgumentNullException>( obj != null );
  //code using obj
}

Sau đó vài giờ tôi viết một hàm khác gọi hàm đầu tiên. Vì mọi thứ vẫn còn mới trong bộ nhớ, tôi quyết định không sao chép hợp đồng, vì tôi biết rằng DoSomethingsẽ kiểm tra một đối tượng null:

void DoSomethingElse( object obj )
{
  //no Requires here: DoSomething will do that already
  DoSomething( obj );
  //code using obj
}

Vấn đề rõ ràng: DoSomethingElsebây giờ phụ thuộc vào DoSomethingviệc xác minh rằng obj không phải là null. Vì vậy, nên DoSomethingbao giờ quyết định không kiểm tra nữa, hoặc nếu tôi quyết định sử dụng chức năng khác thì obj có thể không được kiểm tra nữa. Điều này dẫn tôi đến việc viết triển khai này sau tất cả:

void DoSomethingElse( object obj )
{
  Contract.Requires<ArgumentNullException>( obj != null );
  DoSomething( obj );
  //code using obj
}

Luôn luôn an toàn, không phải lo lắng, ngoại trừ rằng nếu tình huống phát triển cùng một đối tượng có thể được kiểm tra một số lần và đó là một hình thức sao chép và tất cả chúng ta đều biết rằng điều đó không tốt lắm.

Thực tiễn phổ biến nhất cho tình huống như thế này là gì?


3
ArgumentBullException? Đó là một cái mới :)
CVn

lol @ kỹ năng đánh máy của tôi ... Tôi sẽ chỉnh sửa nó.
stijn

Câu trả lời:


13

Cá nhân tôi sẽ kiểm tra null trong bất kỳ chức năng nào sẽ thất bại nếu nó bị null và không có chức năng nào không.

Vì vậy, trong ví dụ của bạn ở trên, nếu doS SomethingElse () không cần phải hủy đăng ký obj thì tôi sẽ không kiểm tra obj cho null ở đó.

Nếu DoS Something () thực hiện obereference obj thì nó nên kiểm tra null.

Nếu cả hai chức năng bãi bỏ nó thì cả hai nên kiểm tra. Vì vậy, nếu các trình duyệt của DoS SomethingElse obj thì nó sẽ kiểm tra null, nhưng DoS Something vẫn nên kiểm tra null vì nó có thể được gọi từ một đường dẫn khác.

Bằng cách này, bạn có thể để mã khá sạch sẽ và vẫn đảm bảo rằng séc được đặt đúng chỗ.


1
Tôi hoàn toàn đồng ý. Điều kiện tiên quyết của mỗi phương pháp nên tự đứng vững. Tưởng tượng rằng bạn viết lại DoSomething()sao cho điều kiện tiên quyết không còn cần thiết (không thể xảy ra trong trường hợp cụ thể này, nhưng có thể xảy ra trong một tình huống khác) và xóa kiểm tra điều kiện tiên quyết. Bây giờ một số phương pháp dường như hoàn toàn không liên quan đã bị phá vỡ vì điều kiện tiên quyết bị thiếu. Tôi sẽ lấy một chút sao chép mã để rõ ràng hơn về những thất bại kỳ lạ như thế từ mong muốn lưu một vài dòng mã, bất cứ ngày nào.
một CVn

2

Tuyệt quá! Tôi thấy bạn đã tìm hiểu về Hợp đồng mã cho .NET. Hợp đồng mã đi xa hơn nhiều so với các xác nhận trung bình của bạn, trong đó trình kiểm tra tĩnh là ví dụ tốt nhất. Điều này có thể không có sẵn cho bạn nếu bạn chưa cài đặt Visual Studio Premium hoặc cao hơn, nhưng điều quan trọng là phải hiểu ý định đằng sau nó nếu bạn sẽ sử dụng Hợp đồng mã.

Khi bạn áp dụng một hợp đồng cho một chức năng, nó thực sự là một hợp đồng . Chức năng đó đảm bảo hành xử theo hợp đồng, và được đảm bảo chỉ được sử dụng theo quy định của hợp đồng.

Trong ví dụ đã cho của bạn, DoSomethingElse()hàm không tuân theo hợp đồng như được chỉ định bởi DoSomething(), vì null có thể được thông qua và trình kiểm tra tĩnh sẽ chỉ ra vấn đề này. Cách để giải quyết điều này là bằng cách thêm cùng một hợp đồng DoSomethingElse().

Bây giờ, điều này có nghĩa là sẽ có sự trùng lặp, nhưng sự trùng lặp này là cần thiết khi bạn chọn hiển thị chức năng qua hai chức năng. Các hàm này, mặc dù là riêng tư, cũng có thể được gọi từ các vị trí khác nhau trong lớp của bạn, vì vậy cách duy nhất để đảm bảo rằng từ bất kỳ cuộc gọi đã cho nào, đối số sẽ không bao giờ là null là sao chép các hợp đồng.

Điều này sẽ khiến bạn xem xét lại lý do tại sao bạn chia hành vi thành hai chức năng ở vị trí đầu tiên. Theo ý kiến ​​của tôi ( trái với niềm tin phổ biến ), bạn không nên phân chia các chức năng chỉ được gọi từ một nơi . Bằng cách phơi bày việc đóng gói bằng cách áp dụng các hợp đồng, điều này càng trở nên rõ ràng hơn. Có vẻ như tôi đã tìm thấy một cuộc tranh luận thêm cho sự nghiệp của tôi! Cảm ơn bạn! :)


liên quan đến đoạn cuối cùng của bạn: trong mã thực tế, cả hai hàm đều là thành viên của hai lớp khác nhau, đó là lý do tại sao chúng bị chia tách. Ngoài ra, tôi đã ở trong tình huống sau rất nhiều lần: viết một hàm dài, quyết định không tách nó. Sau đó tìm ra rằng một số logic được sao chép ở một nơi khác, vì vậy hãy phân tách nó bằng mọi cách. Hoặc một năm sau đọc lại và thấy nó không thể đọc được, vì vậy hãy chia nó ra. Hoặc khi gỡ lỗi: chức năng phân chia = ít đập vào phím F10. Có nhiều lý do hơn, vì vậy cá nhân tôi thích chia tách hơn, mặc dù điều đó có nghĩa là đôi khi nó quá cực đoan.
stijn

(1) "Sau đó tìm ra rằng một số logic được sao chép ở một nơi khác" . Đó là lý do tại sao tôi thấy điều quan trọng hơn là luôn luôn "phát triển theo API" hơn là phân chia các hàm đơn giản. Liên tục nghĩ về việc tái sử dụng, không chỉ trong lớp hiện tại. (2) "Hoặc một năm sau đọc lại và thấy không thể đọc được" Bởi vì các hàm có tên này tốt hơn? Bạn thậm chí còn dễ đọc hơn nếu bạn sử dụng những gì một người bình luận trên blog của tôi được mô tả là "đoạn văn mã". (3) "chức năng phân chia = ít đập vào phím F10" ... Tôi không hiểu tại sao.
Steven Jeuris

(1) đồng ý (2) khả năng đọc là sở thích cá nhân vì vậy đó không thực sự là điều cần thảo luận với tôi .. (3) trải qua chức năng 20 dòng yêu cầu nhấn F10 20 lần. Đi qua một chức năng có 10 trong số các dòng đó trong một chức năng phân tách đòi hỏi tôi phải lựa chọn chỉ phải nhấn F10 11 lần. Có, trong trường hợp đầu tiên tôi có thể đặt các điểm dừng hoặc chọn 'jumpt cho con trỏ' nhưng đó vẫn là một nỗ lực nhiều hơn trường hợp thứ hai.
stijn

@stijn: (2) đồng ý; p (3) cảm ơn vì đã làm rõ!
Steven Jeuris
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.