Làm thế nào để tốt nhất để tổ chức các tập tin lớp và giao diện?


17

OK .. sau tất cả các cuộc thảo luận Tôi đang thay đổi câu hỏi của mình một chút để phản ánh tốt hơn một ví dụ cụ thể mà tôi đang giải quyết.


Tôi có hai lớp ModelOneModelTwo, Các lớp này thực hiện loại chức năng tương tự nhưng không liên quan đến nhau. Tuy nhiên tôi có một lớp thứ ba CommonFunccó chứa một số chức năng nào đó được thực hiện trong cả hai ModelOneModelTwovà đã được một nhân tố ra theo DRY. Hai mô hình được khởi tạo trong ModelMainlớp (bản thân nó được khởi tạo ở mức cao hơn, v.v. - nhưng tôi đang dừng ở cấp độ này).

Container IoC mà tôi đang sử dụng là Microsoft Unity . Tôi không giả vờ là một chuyên gia về nó, nhưng sự hiểu biết của tôi là bạn đăng ký một bộ giao diện và lớp với container và khi bạn muốn một lớp cụ thể, bạn yêu cầu bộ chứa IoC cho bất kỳ đối tượng nào phù hợp với một giao diện cụ thể. Điều này ngụ ý rằng đối với mọi đối tượng tôi muốn khởi tạo từ Unity, phải có một giao diện phù hợp. Bởi vì mỗi lớp của tôi thực hiện chức năng khác nhau (và không chồng chéo), điều này có nghĩa là có tỷ lệ 1: 1 giữa giao diện và lớp 1 . Tuy nhiên, điều đó không có nghĩa là tôi đang viết một giao diện cho mỗi lớp tôi viết.

Vì vậy, mã khôn ngoan tôi kết thúc với 2 :

public interface ICommonFunc 
{ 
}

public interface IModelOne 
{ 
   ICommonFunc Common { get; } 
   .. 
}

public interface IModelTwo
{ 
   ICommonFunc Common { get; } 
   .. 
}

public interface IModelMain 
{ 
  IModelOne One { get; } 
  IModelTwo Two { get; } 
  ..
}

public class CommonFunc : ICommonFunc { .. }

public class ModelOne : IModelOne { .. }

public class ModelTwo : IModelTwo { .. }

public class ModelMain : IModelMain { .. }

Câu hỏi là về cách tổ chức giải pháp của tôi. Tôi có nên giữ lớp và giao diện với nhau? Hay tôi nên giữ các lớp và giao diện cùng nhau? VÍ DỤ:

Cách 1 - Tổ chức theo tên lớp

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Common
          |   |
          |   |-CommonFunc.cs
          |   |-ICommonFunc.cs
          |
          |-Main
          |   |
          |   |-IModelMain.cs
          |   |-ModelMain.cs
          |
          |-One
          |   |
          |   |-IModelOne.cs
          |   |-ModelOne.cs
          |
          |-Two
              |
              |-IModelTwo.cs
              |-ModelTwo.cs
              |

Tùy chọn 2 - Được tổ chức theo chức năng (hầu hết)

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Common
          |   |
          |   |-CommonFunc.cs
          |   |-ICommonFunc.cs
          |
          |-IModelMain.cs
          |-IModelOne.cs
          |-IModelTwo.cs
          |-ModelMain.cs
          |-ModelOne.cs
          |-ModelTwo.cs
          |

Tùy chọn 3 - Giao diện và triển khai

MySolution
  |
  |-MyProject
      |
      |-Interfaces
      |   |
      |   |-Models
      |   |   |
      |       |-Common
      |       |   |-ICommonFunc.cs
      |       |
      |       |-IModelMain.cs
      |       |-IModelOne.cs
      |       |-IModelTwo.cs
      |
      |-Classes
          | 
          |-Models
          |   |
              |-Common
              |   |-CommonFunc.cs
              |
              |-ModelMain.cs
              |-ModelOne.cs
              |-ModelTwo.cs
              |

Tùy chọn 4 - Lấy ví dụ về chức năng

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Components
          |   |
          |   |-Common
          |   |   |
          |   |   |-CommonFunc.cs
          |   |   |-ICommonFunc.cs
          |   |   
          |   |-IModelOne.cs
          |   |-IModelTwo.cs
          |   |-ModelOne.cs
          |   |-ModelTwo.cs
          |
          |-IModelMain.cs
          |-ModelMain.cs
          |

Tôi sắp xếp tùy chọn không thích 1 vì tên lớp trong đường dẫn. Nhưng vì tôi đang có xu hướng tỷ lệ 1: 1 do sự lựa chọn / sử dụng IoC của tôi (và điều đó có thể gây tranh cãi) nên điều này có lợi thế khi thấy mối quan hệ giữa các tệp.

Tùy chọn 2 hấp dẫn tôi, nhưng bây giờ tôi đã làm vấy bẩn vùng nước giữa ModelMainvà các mô hình phụ.

Tùy chọn 3 hoạt động để tách định nghĩa giao diện khỏi việc triển khai, nhưng bây giờ tôi có các ngắt nhân tạo này trong các tên đường dẫn.

Tùy chọn 4. Tôi đã chọn Tùy chọn 2 và điều chỉnh nó để tách các thành phần khỏi mô hình mẹ.

Có lý do chính đáng để thích cái này hơn cái kia không? Hoặc bất kỳ bố trí tiềm năng khác mà tôi đã bỏ lỡ?


1. Frank đã đưa ra một nhận xét rằng có tỷ lệ 1: 1 trở lại các ngày. ++ và các tệp .cpp. Tôi biết anh ấy đến từ đâu. Sự hiểu biết của tôi về Unity dường như đưa tôi vào góc này, nhưng tôi cũng không chắc chắn làm thế nào để thoát khỏi nó nếu bạn cũng đang theo dõi câu ngạn ngữ của Program to an interface Nhưng đó là một cuộc thảo luận cho một ngày khác.

2. Tôi đã bỏ qua các chi tiết của từng hàm tạo đối tượng. Đây là nơi container IoC tiêm các đối tượng theo yêu cầu.


1
Ừ Nó có mùi rằng bạn có tỷ lệ giao diện / lớp 1: 1. Bạn đang sử dụng container IoC nào? Ninject cung cấp các cơ chế không yêu cầu tỷ lệ 1: 1.
RubberDuck

1
@RubberDuck FWIW Tôi đang sử dụng Unity. Tôi không tự nhận là một chuyên gia trong đó, nhưng nếu các lớp học của tôi được thiết kế tốt với các trách nhiệm duy nhất, làm thế nào để tôi không kết thúc với tỷ lệ 1: 1?
Peter M

Bạn có cần IBase hoặc có thể là cơ sở trừu tượng? Tại sao có IDerivingOne khi nó đã thực hiện IBase? Bạn nên phụ thuộc vào IBase, không xuất phát. Tôi không biết về Unity, nhưng các bộ chứa IoC khác cho phép bạn thực hiện tiêm "nhạy cảm theo ngữ cảnh". Về cơ bản khi Client1cần một IBase, nó cung cấp một Derived1. Khi Client2cần một IBase, IoC cung cấp một Derived2.
RubberDuck

1
Chà, nếu cơ sở là trừu tượng, thì không có lý do gì để có interfacenó. An interfacethực sự chỉ là một lớp trừu tượng với tất cả các thành viên ảo.
RubberDuck

1
RubberDuck là chính xác. Giao diện thừa chỉ đơn giản là gây phiền nhiễu.
Frank Hileman

Câu trả lời:


4

Vì một giao diện tương tự trừu tượng với một lớp cơ sở, hãy sử dụng cùng logic mà bạn sẽ sử dụng cho một lớp cơ sở. Các lớp thực hiện một giao diện có liên quan chặt chẽ với giao diện.

Tôi nghi ngờ bạn sẽ thích một thư mục gọi là "Lớp cơ sở"; hầu hết các nhà phát triển sẽ không muốn điều đó, cũng như một thư mục có tên "Giao diện". Trong c #, các thư mục cũng là không gian tên theo mặc định, làm cho nó khó hiểu gấp đôi.

Cách tiếp cận tốt nhất là suy nghĩ về cách bạn sẽ chia nhỏ các lớp / giao diện nếu bạn phải đặt một số vào một thư viện riêng và sắp xếp các không gian tên / thư mục theo cách tương tự. Các nguyên tắc thiết kế khung .net có các đề xuất không gian tên có thể hữu ích.


Tôi khá quen thuộc với liên kết bạn cung cấp, nhưng tôi không chắc nó liên quan đến tổ chức tập tin như thế nào. Trong khi VS mặc định cho tên đường dẫn cho không gian tên ban đầu, tôi biết rằng đây là một lựa chọn tùy ý. Ngoài ra tôi đã cập nhật mã 'mẫu' và khả năng bố trí.
Peter M

@PeterM Tôi không chắc bạn đang sử dụng IDE nào, nhưng Visual Studio không sử dụng tên thư mục để tự động tạo khai báo không gian tên khi thêm một lớp. Điều này có nghĩa là trong khi bạn có thể có sự khác biệt giữa tên thư mục và tên không gian tên, thì việc làm theo cách đó sẽ khó khăn hơn. Vì vậy, hầu hết mọi người không làm điều đó.
Frank Hileman

Tôi đang sử dụng VS. Và vâng tôi biết đau. Nó mang lại những ký ức về bố cục tệp Visual Source Safe.
Peter M

1
@PeterM Liên quan đến tỷ lệ 1: 1 so với tỷ lệ lớp. Một số công cụ yêu cầu này. Tôi xem đây là một khiếm khuyết của công cụ - .net có đủ khả năng phản chiếu để không cần những hạn chế như vậy trong bất kỳ công cụ nào như vậy.
Frank Hileman

1

Tôi thực hiện cách tiếp cận thứ hai, với một số thư mục / không gian tên khác trong các dự án phức tạp tất nhiên. Nó có nghĩa là tôi hủy ghép nối các giao diện từ các triển khai cụ thể.

Trong một dự án khác, bạn có thể phải biết định nghĩa giao diện, nhưng không nhất thiết phải biết bất kỳ lớp cụ thể nào thực hiện nó - đặc biệt là khi bạn sử dụng bộ chứa IoC. Vì vậy, các dự án khác chỉ cần tham khảo các dự án giao diện, không phải các dự án thực hiện. Điều này có thể giữ cho các tài liệu tham khảo thấp, và có thể ngăn chặn các vấn đề tham chiếu vòng tròn.


Tôi đã sửa đổi ví dụ mã của mình và thêm một số tùy chọn khác
Peter M

1

Như mọi khi với bất kỳ câu hỏi thiết kế nào, điều đầu tiên cần làm là xác định ai là người dùng của bạn. Làm thế nào họ sẽ sử dụng tổ chức của bạn?

Có phải họ là những lập trình viên sẽ sử dụng các lớp học của bạn? Sau đó tách các giao diện khỏi mã có thể là tốt nhất.

Họ có phải là người duy trì mã của bạn không? Sau đó giữ các giao diện và các lớp với nhau có thể là tốt nhất.

Ngồi xuống và tạo ra một số trường hợp sử dụng. Sau đó, tổ chức tốt nhất có thể xuất hiện trước bạn.


-2

Tôi nghĩ tốt hơn là lưu trữ các giao diện trong thư viện riêng biệt. Thứ nhất, loại thiết kế này làm tăng sự phụ thuộc. Đó là lý do tại sao việc thực hiện các giao diện này có thể được thực hiện bởi một ngôn ngữ lập trình khác.

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.