Viết mã có thể kiểm tra vs tránh tính tổng quát đầu cơ


11

Tôi đã đọc một số bài viết trên blog sáng nay và tình cờ thấy bài này :

Nếu lớp duy nhất từng thực hiện giao diện Khách hàng là CustomerImpl, thì bạn không thực sự có tính đa hình và khả năng thay thế bởi vì không có gì trong thực tế để thay thế trong thời gian chạy. Đó là sự giả tạo chung chung.

Điều đó có ý nghĩa với tôi, khi thực hiện một giao diện làm tăng thêm sự phức tạp và, nếu chỉ có một lần thực hiện, người ta có thể lập luận rằng nó thêm sự phức tạp không cần thiết. Viết mã trừu tượng hơn mức cần thiết thường được coi là mùi mã gọi là "tính tổng quát đầu cơ" (cũng được đề cập trong bài đăng).

Nhưng, nếu tôi theo dõi TDD, tôi không thể (dễ dàng) tạo ra các bài kiểm tra nhân đôi mà không có tính tổng quát đầu cơ đó, cho dù dưới hình thức triển khai giao diện hoặc tùy chọn đa hình khác của chúng tôi, làm cho lớp có thể kế thừa và các phương thức của nó trở nên ảo.

Vì vậy, làm thế nào để chúng ta điều hòa sự đánh đổi này? Có đáng để nó được suy đoán chung để tạo điều kiện cho thử nghiệm / TDD không? Nếu bạn đang sử dụng thử nghiệm nhân đôi, liệu những điều này có được tính là lần triển khai thứ hai và do đó làm cho tính tổng quát không còn mang tính đầu cơ không? Bạn có nên xem xét một khung mô phỏng nặng hơn cho phép chế giễu các cộng tác viên cụ thể (ví dụ: Moles so với Moq trong thế giới C #)? Hoặc, bạn có nên thử nghiệm với các lớp cụ thể và viết những gì có thể được coi là thử nghiệm "tích hợp" cho đến khi thiết kế của bạn tự nhiên đòi hỏi tính đa hình?

Tôi tò mò muốn đọc những người khác về vấn đề này - cảm ơn trước ý kiến ​​của bạn.


Cá nhân tôi nghĩ rằng các thực thể không nên bị chế giễu. Tôi chỉ giả định các dịch vụ và những dịch vụ này cần một giao diện trong mọi trường hợp, bởi vì mã miền mã thường không có tham chiếu đến mã nơi các dịch vụ được triển khai.
CodeInChaos

7
Chúng tôi, những người sử dụng các ngôn ngữ được gõ động sẽ cười nhạo bạn về các chuỗi mà các ngôn ngữ được nhập tĩnh của bạn đặt vào bạn. Đây là một điều giúp cho việc kiểm tra đơn vị dễ dàng hơn trong các ngôn ngữ được nhập động, tôi không phải phát triển giao diện để thay thế một đối tượng cho mục đích thử nghiệm.
Winston Ewert

Các giao diện không chỉ được sử dụng để tạo hiệu ứng chung. Chúng được sử dụng cho nhiều mục đích, tách mã của bạn là một trong những mục quan trọng. Điều đó lần lượt làm cho việc kiểm tra mã của bạn trở nên dễ dàng hơn rất nhiều.
Marjan Venema

@WinstonEwert Đó là một lợi ích thú vị của việc gõ động mà trước đây tôi chưa từng xem là một người, như bạn chỉ ra, thường không hoạt động trong các ngôn ngữ được gõ động.
Erik Dietrich

@CodeInChaos Tôi chưa xem xét sự khác biệt cho mục đích của câu hỏi này, mặc dù đó là một sự phân biệt hợp lý để thực hiện. Trong trường hợp này, chúng ta có thể nghĩ về các lớp dịch vụ / khung chỉ với một triển khai (hiện tại). Giả sử tôi có một cơ sở dữ liệu mà tôi truy cập bằng DAO. Tôi có nên không giao diện DAO cho đến khi tôi có một mô hình kiên trì thứ cấp? (Đó dường như là những gì tác giả bài viết trên blog đang ám chỉ)
Erik Dietrich

Câu trả lời:


14

Tôi đã đi và đọc bài viết trên blog, và tôi đồng ý với rất nhiều những gì tác giả nói. Tuy nhiên, nếu bạn đang viết mã của mình bằng các giao diện cho mục đích thử nghiệm đơn vị, tôi sẽ nói rằng việc triển khai giả của giao diện triển khai thứ hai của bạn. Tôi tranh luận rằng nó thực sự không bổ sung nhiều vào cách phức tạp cho mã của bạn, đặc biệt là nếu sự đánh đổi không làm như vậy dẫn đến các lớp của bạn bị liên kết chặt chẽ và khó kiểm tra.


3
Hoàn toàn đúng. Mã kiểm tra một phần của ứng dụng vì bạn cần nó để có được thiết kế, triển khai, bảo trì, v.v. Thực tế là bạn không gửi nó cho khách hàng là không liên quan - nếu có một triển khai thứ hai trong bộ thử nghiệm của bạn, thì tính tổng quát có và bạn nên chứa nó.
Kilian Foth

1
Đây là câu trả lời mà tôi thấy có sức thuyết phục nhất (và @KilianFoth nói thêm rằng liệu mã có vận chuyển lần thực hiện thứ hai hay không). Mặc dù vậy, tôi sẽ không chấp nhận câu trả lời một chút để xem có ai khác bấm chuông không.
Erik Dietrich

Tôi cũng sẽ nói thêm, khi bạn phụ thuộc vào các giao diện trong các bài kiểm tra, tính tổng quát không còn mang tính đầu cơ
Pete

"Không làm như vậy" (sử dụng giao diện) không tự động dẫn đến các lớp của bạn được liên kết chặt chẽ. Nó chỉ không. Ví dụ, trong .NET Framework, có một Streamlớp, nhưng không có khớp nối chặt chẽ.
Luke Puplett

3

Kiểm tra mã nói chung không dễ dàng. Nếu đúng như vậy, chúng ta đã thực hiện tất cả từ lâu rồi và không thực hiện một thỏa thuận nào như vậy chỉ trong vòng 10-15 năm qua. Một trong những khó khăn lớn nhất luôn là việc xác định làm thế nào để kiểm tra mã đã được viết một cách cố kết, và được kiểm tra tốt, và có thể kiểm tra mà không phá vỡ đóng gói. Hiệu trưởng BDD đề nghị chúng tôi tập trung gần như hoàn toàn vào hành vi và theo một số cách dường như cho thấy rằng bạn không thực sự cần phải lo lắng về các chi tiết bên trong ở mức độ lớn như vậy, nhưng điều này thường có thể khiến mọi thứ khá khó kiểm tra nếu có rất nhiều phương pháp riêng thực hiện "công cụ" theo một cách rất ẩn, vì nó có thể làm tăng độ phức tạp chung của bài kiểm tra của bạn để đối phó với tất cả các kết quả có thể xảy ra ở cấp độ công khai hơn.

Mocking có thể giúp ở một mức độ nhất định, nhưng một lần nữa là khá tập trung bên ngoài. Dependency Injection cũng có thể hoạt động khá độc đáo, một lần nữa với giả hoặc kiểm tra nhân đôi, nhưng điều này cũng có thể yêu cầu bạn phơi bày các yếu tố thông qua giao diện, hoặc trực tiếp, mà bạn có thể muốn ẩn đi - điều này đặc biệt đúng nếu bạn muốn có một mức độ bảo mật hoang tưởng tốt đẹp về các lớp nhất định trong hệ thống của bạn.

Đối với tôi, bồi thẩm đoàn vẫn chưa biết liệu có nên thiết kế các lớp học của bạn để dễ kiểm tra hơn không. Điều này có thể tạo ra vấn đề nếu bạn thấy mình cần cung cấp các thử nghiệm mới trong khi duy trì mã kế thừa. Tôi chấp nhận rằng bạn sẽ có thể kiểm tra hoàn toàn mọi thứ trong một hệ thống, nhưng tôi không thích ý tưởng phơi bày - thậm chí gián tiếp - các phần bên trong riêng của một lớp, để tôi có thể viết bài kiểm tra cho chúng.

Đối với tôi, giải pháp luôn là áp dụng một cách tiếp cận khá thực tế và kết hợp một số kỹ thuật để phù hợp với từng tình huống cụ thể. Tôi sử dụng rất nhiều phép thử nhân đôi để thể hiện các đặc tính và hành vi bên trong cho các bài kiểm tra của mình. Tôi chế nhạo tất cả mọi thứ có thể được gắn vào các lớp của tôi và khi nó không ảnh hưởng đến an ninh của các lớp của tôi, tôi sẽ cung cấp một phương tiện để ghi đè hoặc tiêm các hành vi cho mục đích kiểm tra. Tôi thậm chí sẽ xem xét việc cung cấp một giao diện hướng sự kiện nhiều hơn nếu nó sẽ giúp cải thiện khả năng kiểm tra mã

Nơi tôi tìm thấy bất kỳ mã "không thể kiểm tra" nào , tôi tìm xem liệu tôi có thể cấu trúc lại để làm cho mọi thứ dễ kiểm tra hơn không. Nơi bạn có rất nhiều mã riêng đang ẩn sau các công cụ hậu trường, thường thì bạn sẽ thấy các lớp mới đang chờ được chia ra. Các lớp này có thể được sử dụng nội bộ, nhưng thường có thể được kiểm tra độc lập với các hành vi ít riêng tư hơn và thường sau đó ít lớp truy cập và độ phức tạp hơn. Tuy nhiên, một điều tôi cần tránh là viết mã sản xuất với mã kiểm tra được tích hợp sẵn. Việc tạo ra các " vấu kiểm tra " dẫn đến việc bao gồm những điều kinh khủng như vậy if testing then ..., điều đó cho thấy vấn đề kiểm tra chưa được giải mã hoàn toàn và giải quyết chưa đầy đủ.

Bạn có thể thấy hữu ích khi đọc cuốn sách Các mẫu thử nghiệm xUnit của Gerard Meszaros , bao gồm tất cả các loại công cụ này chi tiết hơn nhiều so với tôi có thể đi vào đây. Tôi có thể không làm mọi thứ anh ấy đề nghị, nhưng nó giúp làm rõ một số tình huống thử nghiệm khó khăn hơn để giải quyết. Vào cuối ngày, bạn muốn có thể đáp ứng các yêu cầu thử nghiệm của mình trong khi vẫn áp dụng các thiết kế ưa thích của mình và giúp hiểu rõ hơn về tất cả các tùy chọn để quyết định tốt hơn nơi bạn có thể cần phải thỏa hiệp.


1

Ngôn ngữ bạn sử dụng có cách "chế nhạo" một đối tượng để kiểm tra không? Nếu vậy những giao diện khó chịu có thể biến mất.

Trên một lưu ý khác, có thể có một số lý do để có SimpleInterface và một ComplexThing duy nhất thực hiện nó. Có thể có những phần của ComplexThing mà bạn không muốn người dùng SimpleInterface truy cập được. Không phải lúc nào cũng vì một lập trình viên OO-ish quá nhiệt tình.

Bây giờ tôi sẽ bước đi và để mọi người nhảy vào thực tế rằng mã làm điều này "có mùi" đối với họ.


Có, tôi làm việc bằng ngôn ngữ với các khung mô phỏng hỗ trợ các đối tượng cụ thể. Những công cụ này đòi hỏi mức độ khác nhau của nhảy qua vòng để làm điều đó.
Erik Dietrich

0

Tôi sẽ trả lời trong hai phần:

  1. Bạn không cần giao diện nếu bạn chỉ muốn thử nghiệm. Tôi sử dụng các khung mô phỏng cho mục đích đó (trong Java: Mockito hoặc Easymock). Tôi tin rằng mã bạn thiết kế không nên được sửa đổi cho mục đích thử nghiệm. Viết mã có thể kiểm tra, tương đương với viết mã mô-đun, vì vậy tôi có xu hướng viết mã mô-đun (có thể kiểm tra) và chỉ kiểm tra mã giao diện công cộng.

  2. Tôi đã làm việc trong một dự án Java lớn và tôi trở nên thuyết phục sâu sắc rằng việc sử dụng chỉ đọc (chỉ thu khí) giao diện là các con đường để đi (xin lưu ý rằng tôi là một fan hâm mộ lớn của tính bất biến). Lớp triển khai có thể có setters, nhưng đó là một chi tiết triển khai không nên tiếp xúc với các lớp bên ngoài. Từ góc độ khác, tôi thích sáng tác hơn tính kế thừa (mô đun, nhớ không?), Vì vậy các giao diện cũng có ích ở đây. Tôi sẵn sàng trả chi phí cho sự hào phóng đầu cơ hơn là tự bắn vào chân mình.


0

Tôi đã thấy nhiều lợi thế kể từ khi tôi bắt đầu lập trình nhiều hơn cho một giao diện vượt ra ngoài đa hình.

  • Nó buộc tôi phải suy nghĩ kỹ hơn về giao diện của lớp (đó là các phương thức công khai) trước và cách nó sẽ tương tác với các giao diện của các lớp khác.
  • Nó giúp tôi viết các lớp nhỏ hơn gắn kết hơn và tuân theo hiệu trưởng trách nhiệm duy nhất.
  • Kiểm tra mã của tôi dễ dàng hơn
  • Các lớp tĩnh ít hơn / trạng thái toàn cầu là lớp phải ở mức cá thể
  • Dễ dàng tích hợp và lắp ráp các mảnh lại với nhau trước khi toàn bộ chương trình sẵn sàng
  • Phụ thuộc tiêm, tách đối tượng xây dựng từ logic kinh doanh

Nhiều người sẽ đồng ý rằng nhiều hơn, các lớp nhỏ hơn tốt hơn ít hơn, các lớp lớn hơn. Bạn không cần phải tập trung nhiều như vậy cùng một lúc và mỗi lớp có một mục đích được xác định rõ ràng. Những người khác có thể nói rằng bạn đang thêm vào sự phức tạp bằng cách có nhiều lớp hơn.

Thật tốt khi sử dụng các công cụ để cải thiện năng suất, nhưng tôi nghĩ rằng chỉ dựa vào các khung Mock và như vậy thay vì xây dựng khả năng kiểm tra và mô đun hóa trực tiếp vào mã sẽ dẫn đến mã chất lượng thấp hơn trong thời gian dài.

Nói chung, tôi tin rằng nó đã giúp tôi viết mã chất lượng cao hơn và lợi ích vượt xa mọi hậu quả.

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.