2012-02-07 44 views
5

.net không cho phép triển khai giao diện một phần trong các lớp cơ sở. Như một giảm thiểu tôi đã đến 3 giải pháp thay thế. Hãy giúp tôi quyết định cái nào phổ quát hơn về tái cấu trúc, biên dịch/chạy các lỗi thời gian, dễ đọc. Nhưng trước tiên là một vài ý kiến.C# triển khai giao diện một phần tốt nhất trong lớp cơ sở/trừu tượng

  • Tất nhiên bạn luôn có thể truyền đối tượng đến IFoo và gọi bất kỳ phương thức nào mà không có bất kỳ cảnh báo trình biên dịch nào. Nhưng nó không hợp lý, bạn sẽ không làm điều đó bình thường. Cấu trúc này sẽ không xảy ra do tái cấu trúc.
  • Tôi muốn tách biệt tối đa. Hợp đồng lớp trực tiếp (các phương thức và thuộc tính công khai) nên được tách riêng với các triển khai giao diện. Tôi sử dụng giao diện rất nhiều để tách các giao diện đối tượng.

so sánh của tôi:

  1. BaseClass1/MyClass1:
    • con: Phải tạo trừu tượng ảo trong BaseClass1 cho mỗi phương pháp không được thực hiện các IFoo.
    • con: Bọc phương pháp bổ sung - tác động năng suất nhẹ khi chạy.
  2. BaseClass2/MyClass2:
    • con: không có cảnh báo trình biên dịch nếu không thực hiện Method2 trong MyClass2. Thay vào đó, ngoại lệ thời gian chạy. Tái cấu trúc với vùng phủ sóng thử nghiệm đơn vị kém có khả năng làm mất ổn định mã.
    • con: phải đặt thêm cấu trúc lỗi thời để ngăn chặn cuộc gọi phương thức trực tiếp từ các lớp con.
    • con: Phương pháp 2 là công khai cho BaseClass1 vì vậy nó là một phần của hợp đồng lớp học ngay bây giờ. Phải đặt cấu trúc "lỗi thời" để ngăn chặn cuộc gọi trực tiếp, không thông qua IFoo.
  3. BaseClass3/MyClass3:
    • pro: (So với # 2). Dễ đọc hơn. Bạn thấy rằng MyClass2.Method2 là IFoo thực hiện, không chỉ là một số phương pháp overriden.
public interface IFoo 
{ 
    void Method1(); 
    void Method2(); 
} 
public abstract class BaseClass1 : IFoo 
{ 
    void IFoo.Method1() 
    { 
     //some implementation 
    } 

    void IFoo.Method2() 
    { 
     IFooMethod2(); 
    } 

    protected abstract void IFooMethod2(); 
} 

public class MyClass1 : BaseClass1 
{ 
    [Obsolete("Prohibited direct call from child classes. only inteface implementation")] 
    protected override void IFooMethod2() 
    { 
     //some implementation 
    } 
} 
public abstract class BaseClass2 : IFoo 
{ 
    void IFoo.Method1() 
    { 
     //some implementation 
    } 

    [Obsolete("Prohibited direct call from child classes. only inteface implementation")] 
    public virtual void Method2() 
    { 
     throw new NotSupportedException(); 
    } 
} 

public abstract class MyClass2 : BaseClass2 
{ 
    public override void Method2() 
    { 
     //some implementation 
    } 
} 
public abstract class BaseClass3 : IFoo 
{ 
    void IFoo.Method1() 
    { 
     //some implementation 
    } 

    void IFoo.Method2() 
    { 
     throw new NotSupportedException(); 
    } 
} 

public abstract class MyClass3 : BaseClass3, IFoo 
{ 
    void IFoo.Method2() 
    { 
     //some implementation 
    } 
} 
+6

Đây là mẫu ** rất khó xử bạn đang cố triển khai. Bạn nói * ". NET không cho phép thực hiện một phần giao diện trong các lớp cơ sở." * - Có một lý do cho điều đó. Kiểu mã máy khách mong đợi cái gì đó ** thực hiện một giao diện **, bạn biết đấy, có lẽ ... ** thực hiện giao diện **. Ném ngoại lệ cho các phương pháp không được hỗ trợ như là một vấn đề tất nhiên là * rất * mùi mã xấu ... – Yuck

+0

Đồng ý với Yuck. Nếu bạn có một biến kiểu 'IFoo', bạn thực sự mong đợi tất cả các phương thức' IFoo' được triển khai và có sẵn. Giao diện được thực hiện cho điều đó. – ken2k

+0

Chỉ MyClass1 _must_ thực hiện đầy đủ giao diện. Và nó có. Vấn đề là có nhiều lớp con (tôi đã không đề cập đến nó trước đó), mỗi lớp phải thực hiện IFoo. Nếu không có lớp cơ sở, bạn phải sao chép/dán thực thi Method1, tương đương với tất cả các lớp con.Đây là những gì tôi đang cố gắng tránh. Nhưng thực thi Method2 thì khác trong các lớp con, vì vậy tôi không thể chỉ có một lớp thực hiện cả Method1 và Method2. – user1194528

Trả lời

5

Ok, bạn có thể thử cách sau như BaseClass là trừu tượng:

public interface IFoo 
{ 
    void Method1(); 

    void Method2(); 
} 

public abstract class BaseClass : IFoo 
{ 
    public void Method1() 
    { 
     // Common stuff for all BaseClassX classes 
    } 

    // Abstract method: it ensures IFoo is fully implemented 
    // by all classes that inherit from BaseClass, but doesn't provide 
    // any implementation right here. 
    public abstract void Method2(); 
} 

public class MyClass1 : BaseClass 
{ 
    public override void Method2() 
    { 
     // Specific stuff for MyClass1 
     Console.WriteLine("Class1"); 
    } 
} 

public class MyClass2 : BaseClass 
{ 
    public override void Method2() 
    { 
     // Specific stuff for MyClass2 
     Console.WriteLine("Class2"); 
    } 
} 

private static void Main(string[] args) 
{ 
    IFoo test1 = new MyClass1(); 
    IFoo test2 = new MyClass2(); 

    test1.Method2(); 
    test2.Method2(); 

    Console.ReadKey(); 
} 
+0

Đây là biến thể của tôi # 2 ngoại trừ bất lợi đầu tiên. Vẫn áp dụng: 1. phải đặt thêm cấu trúc lỗi thời để ngăn chặn cuộc gọi phương thức trực tiếp từ các lớp con. Trong trường hợp của bạn không có lỗi thời, do đó, cuộc gọi trực tiếp từ lớp con sẽ không tạo ra cảnh báo thời gian biên dịch 2. Method2 là công khai cho BaseClass1 vì vậy nó là một phần của hợp đồng lớp bây giờ. Phải đặt cấu trúc "lỗi thời" để ngăn chặn cuộc gọi trực tiếp, không thông qua IFoo. 3. Ít dễ đọc hơn. Bạn không thấy rằng Method2 là IFoo thực hiện. – user1194528

+0

Đọc các bình luận bên dưới câu hỏi, điều này dường như là cách chính xác để giải quyết vấn đề. Việc triển khai thực hiện trừu tượng 'Method2()' trong 'BaseClass' đảm bảo rằng' IFoo' được triển khai đầy đủ, đồng thời buộc tất cả các lớp bắt nguồn từ 'BaseClass' để thực hiện' Method2() '. Nó cũng có thể được thêm vào mà các lớp dẫn xuất sẽ _not_ có thể ghi đè lên 'Method1()' (nhưng chúng sẽ có thể _hide_ nó (sử dụng từ khoá 'new')). – Nailuj

+0

@ user1194528 Sự cố thực tế của bạn với mã ở trên là gì? Tại sao bạn muốn đặt chú thích quá cũ? – ken2k

6

Nó là vô cùng xấu để thiết kế một lớp điều đó không không triển khai định nghĩa đúng d hợp đồng. Nó là cực đoan bởi vì trước tiên bạn nói rằng một lớp học có khả năng làm điều gì đó. Bạn rõ ràng làm nổi bật rằng lớp có thể làm công cụ, nhưng sau đó trong mã bạn nói nahh, vặn nó, lớp này có thể sống mà không cần thực hiện. Trình biên dịch rất khôn ngoan yêu cầu bạn thực hiện hợp đồng, nhưng nó được để bạn quyết định.

Dưới đây là một số giải pháp chung

Bad giải pháp

  • Ném một ngoại lệ (NonImplementedException hoặc NotSupportedException, xem sample)
  • Khai báo nó như là lỗi thời (thiết kế nó tốt ngay từ đầu)

Tốt hơn giải pháp

  • Explicit giao diện thực hiện, nhưng bạn vẫn thực hiện nó (chỉ loại giấu nó)

giải pháp tốt nhất

  • Sử dụng giao diện phân (chia giao diện chất béo của bạn thành mỏng và những người có thể quản lý nhiều hơn)
+1

Tôi nghĩ về phân biệt giao diện, nhưng vấn đề là thiết kế giao diện xuất phát từ nhu cầu của tầng lớp khách, không phải lớp máy chủ. Lớp khách hàng không muốn biết chi tiết triển khai. Tại sao nó nên biết rằng một số phần nếu giao diện này được thực hiện trong lớp cơ sở và khác ở trẻ em? Nó cũng rất mong manh có thể có hai phân cấp lớp thực hiện giao diện này và chúng khác nhau theo cách thức thực hiện được phân chia giữa lớp cơ sở và lớp con. – user1194528

0

Tôi muốn đề nghị có lớp cơ sở trừu tượng thực hiện giao diện với các phương thức gọi phương thức protected abstract, như trong ví dụ đầu tiên của bạn, ngoại trừ các phương thức mà một số lớp dẫn xuất có thể không thực hiện (theo sau "ném mọi thứ vào IList nhưng không có tất cả các phương pháp thực sự làm việc "mô hình"; những thứ đó có thể là số protected virtual khai quật để ném NotSupportedException.

Lưu ý rằng tùy thuộc vào lớp con có thể hiển thị bất kỳ thành viên cụ thể nào của giao diện dưới dạng thành viên công khai được đặt tên (có thể gọi thành viên trừu tượng thích hợp) không.

Mẫu thích hợp trong VB.net sẽ giống như MustOverride Sub IFoo_Method1() Implements IFoo.Method1, điều này sẽ tránh được phí gọi hàm bổ sung, nhưng C# không cung cấp bất kỳ phương tiện nào để triển khai giao diện với thành viên được bảo vệ. Sử dụng cách thực hiện giao diện rõ ràng cho bất kỳ phương thức nào có thể phải ghi đè trong lớp con là hơi khốc liệt, vì không thể thực hiện lại giao diện của con để chuỗi triển khai thực hiện của cha mẹ.

+0

>> không thể thực hiện lại giao diện của trẻ để chuỗi triển khai của cha mẹ. - điểm tốt. Từ quan điểm này, tốt hơn nên khai báo tất cả các phương thức như được bảo vệ (+ abstract cho những phương thức, không được triển khai) (ken2k variant). Cảm ơn 2 tất cả cho bạn suy nghĩ. Bây giờ tôi có thể đưa ra quyết định sáng suốt hơn. – user1194528

8

Tôi thích phiên bản này, lớp cơ sở không thể được khởi tạo vì trừu tượng của nó, lớp dẫn xuất phải liệt kê IFoo trong khai báo của nó hoặc người khác sẽ không triển khai giao diện và sau đó tự chịu trách nhiệm thực hiện phần còn lại của giao diện. Một nhược điểm tôi có thể thấy là bạn không thể thực hiện một cách rõ ràng các phương thức giao diện trong lớp cơ sở (tức là không có IFoo: Method1), nhưng nếu không thì đây là phiên bản khá thấp.

public interface IFoo 
{ 
    void Method1(); 
    void Method2(); 
} 

public abstract class BaseClass1 
{ 
    public void Method1() 
    { 
     //some implementation 
    } 
} 

public class MyClass1 : BaseClass1, IFoo 
{ 
    public void Method2() 
    { 
     //some implementation 
    } 
} 
Các vấn đề liên quan