2011-01-25 43 views
6

Tôi phát hiện một tiêu chuẩn mã hóa C# được đề xuất có nội dung "Thử cung cấp giao diện với tất cả các lớp trừu tượng". Có ai biết lý do cho việc này không?Giao diện/Mã hóa lớp trừu tượng Tiêu chuẩn

+4

Bạn có thể cung cấp liên kết nơi bạn thấy đề xuất này không? –

+0

@Jay: Google dẫn tôi tới đây: http://docs.google.com/viewer?a=v&q=cache:reQ1URsy3X0J:www.danrigsby.com/Files/csharpcodingstandards.doc+%22Try+to+offer+an+interface + với + tất cả + trừu tượng + lớp% 22 & hl = vi & gl = vn & pid = bl & srcid = ADGEESi9ylZVo1InFp604CL926OHZ_QLr4taUT6O0DaojgsrS__V7ID4pUJw7gtdp5s0u0NwQYmAAjMILDxJsphtH_XmeFmYhunknO8UxD5il580OTtSD-yl94xtz0uw6VhUycN5eVvv & sig = AHIEtbQyKOvKQE69l_EVLO_3XNqJLeCKFw –

+0

và giả sử các hướng dẫn tôi liên kết với trên là trên thực tế cùng những người thân mà bạn đọc, điểm đầu tiên được thực hiện theo các "giao diện "phần làm phiền tôi nhiều hơn:" Luôn thích giao diện hơn các lớp trừu tượng. " Và đó là khuyến cáo thậm chí còn mạnh mẽ hơn so với các hướng dẫn bạn đang trích dẫn. –

Trả lời

15

.NET Framework Design Guidelines có một số điều thú vị để nói về giao diện và lớp trừu tượng.

Đặc biệt, họ lưu ý rằng giao diện có nhược điểm lớn là ít linh hoạt hơn so với các lớp khi nói đến sự tiến hóa của API. Khi bạn gửi một giao diện, các thành viên của nó sẽ được sửa vĩnh viễn và mọi bổ sung sẽ phá vỡ khả năng tương thích với các loại hiện có triển khai giao diện đó. Trong khi đó, việc vận chuyển một lớp học mang lại sự linh hoạt hơn nhiều. Thành viên có thể được thêm vào bất kỳ lúc nào, ngay cả sau khi phiên bản ban đầu đã được giao, miễn là chúng không trừu tượng. Bất kỳ lớp học có nguồn gốc hiện tại có thể tiếp tục làm việc không thay đổi. Lớp trừu tượng System.IO.Stream được cung cấp trong khung được đưa ra làm ví dụ. Ban đầu nó được vận chuyển mà không hỗ trợ cho thời gian chờ các hoạt động I/O đang chờ xử lý, nhưng phiên bản 2.0 đã có thể thêm các thành viên hỗ trợ tính năng này, ngay cả từ các lớp con hiện có.

Do đó, việc có giao diện tương ứng cho mỗi lớp cơ sở trừu tượng sẽ cung cấp thêm một số lợi ích bổ sung. Giao diện không thể được hiển thị công khai hoặc bạn bị bỏ lại ở chế độ xem hình vuông về mặt phiên bản. Và nếu bạn chỉ phơi bày lớp cơ sở trừu tượng, có ít thu được bằng cách có giao diện ngay từ đầu.

Ngoài ra, điểm thường được thực hiện có lợi cho giao diện mà họ cho phép tách hợp đồng khỏi triển khai.Krzysztof Cwalina lập luận rằng tuyên bố này là rất quan trọng: nó không chính xác giả định rằng bạn không thể tách các hợp đồng khỏi việc thực hiện bằng cách sử dụng các lớp. Bằng cách viết các lớp trừu tượng cư trú trong một hội đồng riêng biệt từ việc triển khai cụ thể của họ, thật dễ dàng để đạt được cùng một đức tính tách biệt. Ông viết:

Tôi thường nghe mọi người nói rằng giao diện chỉ định hợp đồng. Tôi tin rằng đây là một huyền thoại nguy hiểm. Giao diện, một mình, không chỉ định nhiều hơn cú pháp cần thiết để sử dụng một đối tượng. Huyền thoại giao diện-hợp đồng làm cho mọi người làm điều sai trái khi cố gắng tách các hợp đồng khỏi việc thực hiện, đó là một thực hành kỹ thuật tuyệt vời. Giao diện riêng biệt cú pháp từ thực hiện, mà không phải là hữu ích, và huyền thoại cung cấp một cảm giác sai lầm của làm đúng kỹ thuật. Trong thực tế, hợp đồng là ngữ nghĩa, và những thực tế này có thể được thể hiện rõ ràng với một số thực hiện.

Nói chung, hướng dẫn được cung cấp là KHÔNG ưu tiên xác định các lớp học qua giao diện. Một lần nữa, Krzysztof nhận xét:

Trong suốt ba phiên bản của .NET Framework, tôi đã nói về hướng dẫn này với một vài nhà phát triển trong nhóm của chúng tôi. Nhiều người trong số họ, bao gồm cả những người ban đầu không đồng ý với hướng dẫn, đã nói rằng họ rất tiếc khi đã chuyển một số API làm giao diện. Tôi đã không nghe nói về một trường hợp, trong đó ai đó hối tiếc rằng họ đã chuyển một lớp.

Một châm thứ hai lập luận rằng một DO sử dụng lớp trừu tượng thay vì giao diện để tách các hợp đồng từ việc triển khai. Vấn đề ở đây là các lớp trừu tượng được thiết kế chính xác vẫn cho phép cùng một mức tách giữa hợp đồng và thực hiện dưới dạng các giao diện. Do đó, quan điểm cá nhân của Brian Pepin là:

Một điều tôi đã bắt đầu thực hiện là đưa nhiều hợp đồng vào lớp trừu tượng nhất có thể. Ví dụ, tôi có thể muốn có bốn quá tải đến một phương thức mà mỗi quá tải cung cấp một tập các thông số ngày càng phức tạp. Cách tốt nhất để làm điều này là cung cấp một triển khai thực hiện nonvirtual của các phương thức này trên lớp trừu tượng và triển khai tất cả các tuyến đường đến một phương thức trừu tượng được bảo vệ để cung cấp việc triển khai thực tế. Bằng cách này, bạn có thể viết tất cả logic kiểm tra đối số nhàm chán một lần. Các nhà phát triển muốn triển khai lớp học của bạn sẽ cảm ơn bạn.

Có lẽ người ta sẽ cố gắng hết sức bằng cách truy cập lại các oft-chào hàng "quy tắc" mà một lớp được thừa kế chỉ ra một IS-Một mối quan hệ với lớp cơ sở, trong khi một lớp thực hiện một giao diện có một mối quan hệ CAN-DO với giao diện đó. Để làm cho một yêu cầu bồi thường nên luôn luôn mã cả một giao diện và một lớp cơ sở trừu tượng, độc lập với lý do cụ thể để làm như vậy, dường như bỏ lỡ điểm.

+0

+1 Tôi thích lời giải thích của bạn hơn. Tôi vẫn đang cố gắng hiểu tại sao Glav nói nó làm cho việc thử nghiệm với Rhino dễ dàng hơn. –

+0

@Harvey: Cảm ơn. Tôi thực sự không biết Glav có nghĩa là gì, bởi vì tôi chưa bao giờ sử dụng bất kỳ công cụ thử nghiệm nào. Đó là lý do tại sao tôi đảm bảo làm rõ có thể có ngoại lệ dựa trên những lý do cụ thể. –

3

Nếu không nhìn vào bài viết gốc, tôi sẽ đoán rằng tác giả ban đầu được đề nghị nó cho testability và cho phép mocking dễ dàng của lớp với các công cụ như Moq, RhinoMocks, vv

+0

Glav là đúng. Ít nhất với Rhino Mocks, thử nghiệm các phương pháp cụ thể là một PITA. Do đó, việc có lớp trừu tượng thực hiện một giao diện cho phép khả năng thử nghiệm lớn hơn nhiều. – Simone

0

Tôi nghĩ đó là quá sớm để nói liệu một giao diện là cần thiết hay không theo nghĩa chung. Vì vậy, tôi nghĩ chúng ta không nên đặt "Hãy thử cung cấp một giao diện với tất cả các lớp trừu tượng" như một tiêu chuẩn mã hóa trừ khi tiêu chuẩn mã hóa đó bao gồm nhiều chi tiết hơn khi áp dụng quy tắc này.

Nếu tôi sẽ không sử dụng giao diện nào cả, tôi vẫn cần xác định giao diện chỉ để đáp ứng tiêu chuẩn mã hóa?

1

tôi luôn luôn hiểu Interface-Driven Design (IDD) để bao gồm các bước sau đây trong việc tạo ra một lớp bê tông (ở dạng tinh khiết của nó, cho các lớp học không tầm thường):

  1. Tạo một giao diện để mô tả các thuộc tính và hành vi của các đối tượng của bạn phải thể hiện, nhưng không phải cách thức hoạt động.
  2. Tạo lớp cơ sở trừu tượng làm triển khai chính của giao diện. Thực hiện bất kỳ chức năng nào được yêu cầu bởi giao diện, nhưng không có khả năng khác nhau giữa các triển khai cụ thể. Cũng cung cấp các cài đặt mặc định thích hợp (virtual) cho các thành viên không chắc (nhưng có thể) để thay đổi. Bạn cũng có thể cung cấp các nhà xây dựng thích hợp (một cái gì đó không thể ở cấp độ giao diện). Đánh dấu tất cả các thành viên giao diện khác là trừu tượng.
  3. Tạo lớp bê tông của bạn từ lớp trừu tượng, ghi đè một tập con của các thành viên ban đầu được xác định bởi giao diện.

Quy trình trên, trong khi kéo dài, đảm bảo tuân thủ tối đa hợp đồng ban đầu bạn đặt xuống, đồng thời giảm thiểu mã dư thừa trong các triển khai thay thế.

Đây là lý do tại sao tôi thường ghép một lớp trừu tượng với giao diện.

+0

Không rõ lợi thế của giao diện là gì theo mô hình này. Bạn nhận được lợi ích gì từ việc tạo giao diện đầu tiên, thay vì chỉ viết lớp cơ sở trừu tượng như là triển khai thực hiện? –

+0

Nếu chúng tôi chỉ có thể làm như vậy mọi lúc! – XIVSolutions

+0

@Cody Đó là một thái độ thuần khiết. Nếu bạn chấp nhận rằng cách duy nhất (đúng) để quyết định hành vi là thông qua một giao diện, thì bắt đầu từ một lớp trừu tượng được xem như một cách tiếp cận yếu. Ngoài ra, giao diện trình bày các thành viên khi chúng được dự định được gọi, trong khi lớp trừu tượng trình bày chúng khi chúng được dự định được thực hiện/ghi đè - một sự khác biệt tinh tế trong ngữ nghĩa. –

0

Phát triển theo hướng kiểm tra (TDD) là một lý do chính khiến bạn muốn thực hiện việc này. Nếu bạn có một lớp phụ thuộc trực tiếp vào lớp trừu tượng của bạn, bạn không thể kiểm tra nó mà không cần viết một lớp con có thể được khởi tạo trong các bài kiểm tra đơn vị của bạn. Tuy nhiên, nếu lớp phụ thuộc của bạn chỉ phụ thuộc vào một giao diện thì bạn có thể cung cấp một 'ví dụ' của điều này một cách dễ dàng bằng cách sử dụng một khung mocking như Rhino Mocks, NMock, v.v.

Cuối cùng tôi nghĩ nó sẽ giảm xuống như thế nào bạn gửi sản phẩm của bạn. Chúng tôi chỉ bao giờ gửi các tệp nhị phân và khách hàng không bao giờ mở rộng công việc của chúng tôi. Bên trong chúng ta có giao diện cho mọi thứ khá nhiều để các lớp có thể được cô lập hoàn toàn để kiểm thử đơn vị.Điều này mang lại lợi ích to lớn cho việc tái cấu trúc và kiểm tra hồi quy!

EDIT: cập nhật với ví dụ

Hãy xem xét đoạn mã sau trong một thử nghiệm đơn vị:

// doesn't work - can't instantiate BaseClass directly 
var target = new ClassForTesting(new BaseClass());  

// where we only rely on interface can easily generate mock in our tests 
var targetWithInterface = new ClassForTestingWithInterface(MockRepository.GenerateStub<ISomeInterface>()); 

nơi phiên bản lớp trừu tượng là:

// dependent class using an abstract class 
public abstract class BaseClass 
{ 
    public abstract void SomeMethod(); 
} 

public class ClassForTesting 
{ 
    public BaseClass SomeMember { get; private set; } 

    public ClassForTesting(BaseClass baseClass) 
    { 
     if (baseClass == null) throw new ArgumentNullException("baseClass"); 
     SomeMember = baseClass; 
    } 
} 

và những thứ tương tự nhưng sử dụng giao diện là:

public interface ISomeInterface 
{ 
    void SomeMethod(); 
} 

public abstract class BaseClassWithInterface : ISomeInterface 
{ 
    public abstract void SomeMethod(); 
} 

public class ClassForTestingWithInterface 
{ 
    public ISomeInterface SomeMember { get; private set; } 

    public ClassForTestingWithInterface(ISomeInterface baseClass) {...} 
} 
+0

Làm thế nào về điều này? Có vẻ như anh ta có thể giả lập một lớp trừu tượng sử dụng Rhino http://stackoverflow.com/questions/620894/mock-abstract-class-default-behaviour-with-rhino –

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