2010-08-23 41 views
7

Tôi gặp vấn đề về thiết kế mà tôi muốn giải quyết. Tôi có một giao diện, cho phép gọi nó là IProtocol, được triển khai bởi hai lớp riêng biệt. Chúng tôi đang xem xét hơn 600 dòng mã ở đây. Đại đa số những thứ họ làm là như nhau, trừ đối với một số lĩnh vực cụ thể, như DiffStuff();Vấn đề thiết kế - Thừa kế đúng cách để đơn giản hóa mã này?

cấu trúc hiện tại là một cái gì đó như thế này:

public class Protocol1 : IProtocol 
{ 
    MyInterfaceMethod1() 
    { 
    Same1(); 
    DiffStuff(); 
    Same2(); 
    } 
} 

public class Protocol2 : IProtocol 
{ 
    MyInterfaceMethod1() 
    { 
    Same1(); 
    Same2(); 
    } 
} 

Tôi lo ngại có lỗi sao chép và dán ông vấn đề cổ điển của mã trùng lặp nếu tôi giữ hai giao thức riêng biệt. Chúng tôi đang nói về một 600 dòng mã đầy đủ, không phải là một số phương pháp đơn giản.

tôi đang xem xét thay đổi việc thực hiện Protocol1 kế thừa từ protocol2, như thế này (Protocol2 chủ yếu sẽ giữ nguyên, ngoại trừ tôi phải quấn Same1()Same2() thành phương pháp tư nhân.)

public class Protocol1 : Protocol2 
{ 
    void Same1() 
    { 
    base.Same1(); 
    } 

    void Same2() 
    { 
    base.Same2(); 
    } 

    MyInterfaceMethod1() 
    { 
    Same1(); 
    DiffStuff(); 
    Same2(); 
    } 
} 

Đây có phải là cách phù hợp để giải quyết vấn đề này không?

Chỉnh sửa: Nhiều người đã giúp tôi với câu hỏi này, cảm ơn sự hiểu biết rõ ràng. Trong trường hợp của tôi, hai đối tượng không cùng loại, mặc dù phần lớn việc triển khai của chúng được chia sẻ, vì vậy tôi đã đi với Bobby's suggestion để sử dụng lớp cơ sở trừu tượng, tạo các phương thức nhỏ để đóng gói các thay đổi giữa các lớp. Bổ sung Nhờ:

  • jloubert
  • Hans passant
  • Jeff xương ức
+10

Tại sao không sử dụng một lớp trừu tượng mà xác định phương pháp chia sẻ và định nghĩa trừu tượng cho những người cần thiết mà không được thực hiện ? – alternative

+0

Hãy xem "Mã sạch" của Robet C. Martin –

+2

Bạn chỉ nên tạo 'Protocol1' kế thừa từ' Protocol2' nếu điều đó thực sự hợp lý. Bạn cần phải hỏi, "là mỗi Protocol1 cũng là một thể hiện của Protocol2, trong cùng một ý nghĩa rằng mỗi con mèo là một động vật có vú?" Nếu có, hãy làm những gì bạn đang suy nghĩ. Nếu không, tôi sẽ đi với một lớp cơ sở trừu tượng, như thể hiện trong câu trả lời của Bobby. – jloubert

Trả lời

7
/// <summary> 
    /// IProtocol interface 
    /// </summary> 
    public interface IProtocol 
    { 
     void MyInterfaceMethod1(); 
     void Same1(); 
     void Same2(); 
    } 

sau đó ...

public abstract class ProtocolBase : IProtocol 
{ 
    #region IProtocol Members 

    public void MyInterfaceMethod1() 
    { 
     // Implementation elided... 
    } 

    public void Same1() 
    { 
     // Implementation elided... 
    } 

    public void Same2() 
    { 
     // Implementation elided... 
    } 

    public abstract void DiffStuff(); 

    #endregion 
} 

cuối cùng ...

public sealed class Protocol1 : ProtocolBase 
{ 
    public override void DiffStuff() 
    { 
     // Implementation elided... 
    } 
} 

public sealed class Protocol2 : ProtocolBase 
{ 
    public override void DiffStuff() 
    { 
     // Implementation elided... 
    } 
} 
5

Không hẳn. Không có vấn đề gì khi thêm các phương thức Same1 và Same2, bạn kế thừa chúng từ ProtocolBase. Và DiffStuff() nên là một phương pháp ảo để bạn có thể ghi đè lên nó và cho nó hành vi khác nhau.

-1

Tôi đồng ý với MathEpic. Tôi sẽ sử dụng mẫu Template method.

+3

(-1) Vui lòng đăng bình luận của bạn dưới dạng * bình luận *. – DevinB

0

tôi thiết kế tốt hơn (theo ý kiến ​​của tôi)

public abstract class Protocol_common : IProtocol 
{ 
    MyInterfaceMethod1() 
    { 
    Same1(); 
    DiffStuff(); 
    Same2(); 
    } 

    abstract void DiffStuff(); 

} 

public class Protocol1 : Protocol_common 
{ 
    DiffStuff() 
    { 
     /// stuff here. 
    } 
} 

public class Protocol2 : Protocol_common 
{ 
    DiffStuff() 
    { 
     /// nothing here. 
    } 
} 

(Đó là thực tế hơn pseudo-code hơn thích C#, nhưng tôi nhấn điểm cao)

+1

Không nên Protocol1 và kế thừa từ Protocol_common và Protocol_common phải trừu tượng? –

+0

D'oh! để copy'n'paste nhiều –

+0

Việc triển khai MyInterfaceMethod1 cho Protocol1 và Protocol2 hơi khác nhau (Protocol2 không thực sự gọi DiffStuff), vì vậy điều này cần phải được tạo trừu tượng và được thực hiện riêng biệt cho mỗi lớp dẫn xuất. Hoặc có thể sử dụng các phương thức ảo và xem xét một trong các triển khai DiffStuff là hành vi mặc định. – fletcher

4

Bạn đang hết sức gần gũi với mô tả template method pattern , có phả hệ dài như một giải pháp hiệu quả cho các vấn đề như của bạn.

Tuy nhiên, bạn nên cân nhắc sử dụng bố cục thay vì thừa kế. The two approaches offer different advantages and disadvantages, nhưng thành phần thường (thường là?) Tốt hơn *:

public class Protocol { 

     private ISpecialHandler specialHandler; 

     public Protocol() {} 

     public Protocol(ISpecialHandler specialHandler) { 
      this.specialHandler = specialHandler; 
     } 

     void Same1() {} 
     void Same2() {} 

     public void DoStuff() { 
      this.Same1(); 
      if (this.specialHandler != null) { 
       this.specialHandler.DoStuff(); 
      } 
      this.Same2(); 
     } 
} 

Người gọi sau đó có thể vượt qua trong trường hợp đối tượng (strategies) cung cấp các thuật toán chuyên dụng để xử lý bất cứ trường hợp là ở bàn tay:

Protocol protocol1 = new Protocol(new DiffStuffHandler()); 
protocol1.DoStuff(); 

* Xem Patterns I Hate #2: Template Method cho một chi tiết giải thích lý do tại sao bố cục thường tốt hơn so với thừa kế trong trường hợp của bạn.

+0

Điều này có lẽ phù hợp hơn với Giao thức hơn là thành phần đảo ngược mà tôi đã đề xuất. –

+0

Đề nghị nhỏ tôi sẽ làm, là để làm cho ISpecialHandler một tài sản hơn là dùng nó trong constructor. Điều đó cho phép người gọi API của bạn biết cụ thể rằng ISpecialHandler là tùy chọn. – BFree

0

Bạn có thể muốn xem xét liệu loại mô hình hoạt động:

public class ProtocolHelper 
{ 
    public void Same1() {} 
    public void Same2() {} 
} 

public class Protocol1 : IProtocol 
{ 
    private readonly ProtocolHelper _helper = new ProtocolHelper(); 

    void MyInterfaceMethod1() 
    { 
     _helper.Same1(); 
     DiffStuff(); 
     _helper.Same2(); 
    } 
} 

Bạn có thể nói nếu điều này có ý nghĩa bằng cách nhìn thấy nếu bạn có thể đưa ra một tên tốt cho lớp 'ProtocolHelper'. Nếu một cái tên tự nhiên nảy sinh từ logic của bạn, thì đây là cách tốt để chia lớp. Bạn có thể cần phải chuyển một số phụ thuộc (chẳng hạn như trường riêng) làm tham số cho các phương thức để thực hiện công việc này.

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