Đó có phải là một cách thực hành tốt để tạo ClassCollection của Class khác không?


35

Hãy nói rằng tôi có một Carlớp học:

public class Car
{
    public string Engine { get; set; }
    public string Seat { get; set; }
    public string Tires { get; set; }
}

Hãy nói rằng chúng tôi đang tạo ra một hệ thống về một bãi đậu xe, tôi sẽ sử dụng rất nhiều Carlớp, vì vậy chúng tôi tạo một CarCollectionlớp, nó có thể có một vài phương thức quảng cáo như FindCarByModel:

public class CarCollection
{
    public List<Car> Cars { get; set; }

    public Car FindCarByModel(string model)
    {
        // code here
        return new Car();
    }
}

Nếu tôi đang tham gia một lớp học ParkingLot, thực hành tốt nhất là gì?

Lựa chọn 1:

public class ParkingLot
{
    public List<Car> Cars { get; set; }
    //some other properties
}

Lựa chọn 2:

public class ParkingLot
{
    public CarCollection Cars { get; set; }
    //some other properties
}

Nó thậm chí là một thực hành tốt để tạo ra một cái ClassCollectionkhác Class?


Lợi ích gì bạn nhận thấy khi vượt qua CarCollectionhơn là List<Car>xung quanh? Đặc biệt là CarCollection không mở rộng lớp Danh sách sao lưu hoặc thậm chí triển khai giao diện Bộ sưu tập (Tôi chắc chắn rằng C # có những thứ tương tự).

Danh sách <T> đã triển khai IList <T>, ICollection <T>, IList, ICollection, IReadOnlyList <T>, IReadOnlyCollection <T>, IEnumerable <T> và IEnumerable ... Bên cạnh đó, tôi có thể sử dụng Linq ...
Luis

Nhưng public class CarCollectionkhông triển khai IList hoặc ICollection, v.v ... do đó bạn không thể chuyển nó sang một thứ phù hợp với danh sách. Nó tuyên bố như là một phần của tên của nó rằng nó là một bộ sưu tập, nhưng không thực hiện bất kỳ phương pháp nào trong số đó.

1
là một câu hỏi 6 tuổi tôi thấy không có ai đề cập rằng đây là một thực tế phổ biến trong DDD. Bất kỳ bộ sưu tập nên được trừu tượng hóa thành một bộ sưu tập tùy chỉnh. Ví dụ: bạn muốn tính giá trị của một nhóm ô tô. Bạn sẽ đặt logic đó ở đâu? trong một dịch vụ? Hoặc trong DDD bạn sẽ có CarColectionmột TotalTradeValuetài sản trên đó. DDD không phải là cách duy nhất để thiết kế hệ thống, chỉ cần chỉ ra nó là một tùy chọn.
Storm Muller

1
Chính xác, Những điều này thực sự là gốc rễ tổng hợp như được đề xuất trong DDD, Chỉ có điều là DDD có lẽ sẽ không khuyên bạn nên gọi nó là một bộ sưu tập xe hơi. Thay vì sử dụng một số tên như Bãi đỗ xe hoặc Khu vực làm việc, v.v.
Tên mã Jack

Câu trả lời:


40

Trước các khái quát trong .NET, thông thường là tạo ra các bộ sưu tập 'gõ' để bạn có thể có class CarCollectionmọi loại bạn cần để nhóm. Trong .NET 2.0 với sự ra mắt của Generics, một lớp mới List<T>đã được giới thiệu giúp bạn tiết kiệm được việc tạo CarCollectionv.v. như bạn có thể tạo List<Car>.

Hầu hết thời gian, bạn sẽ thấy điều đó List<T>là đủ cho mục đích của mình, tuy nhiên có thể đôi khi bạn muốn có hành vi cụ thể trong bộ sưu tập của mình, nếu bạn tin rằng đây là trường hợp, bạn có một vài lựa chọn:

  • Tạo một lớp đóng gói List<T>chẳng hạnpublic class CarCollection { private List<Car> cars = new List<Car>(); public void Add(Car car) { this.cars.Add(car); }}
  • Tạo một bộ sưu tập tùy chỉnh public class CarCollection : CollectionBase<Car> {}

Nếu bạn đi theo cách tiếp cận đóng gói, tối thiểu bạn nên để lộ điều tra viên để bạn sẽ khai báo như sau:

public class CarCollection : IEnumerable<Car>
{
    private List<Car> cars = new List<Car>();

    public IEnumerator<Car> GetEnumerator() { return this.cars.GetEnumerator(); }
}

Không làm điều đó, bạn không thể thực hiện một foreachbộ sưu tập.

Một số lý do bạn có thể muốn tạo một bộ sưu tập tùy chỉnh là:

  • Bạn không muốn phơi bày đầy đủ tất cả các phương thức trong IList<T>hoặcICollection<T>
  • Bạn muốn thực hiện các hành động bổ sung khi thêm hoặc xóa một mục khỏi bộ sưu tập

Có phải là thực hành tốt? tốt, điều đó phụ thuộc vào lý do tại sao bạn làm việc đó, nếu đó là một trong những lý do tôi đã liệt kê ở trên thì có.

Microsoft làm điều đó khá thường xuyên, đây là một số ví dụ khá gần đây:

Đối với FindByphương pháp của bạn , tôi sẽ cố gắng đưa chúng vào các phương thức mở rộng để chúng có thể được sử dụng để chống lại bất kỳ bộ sưu tập nào có chứa ô tô:

public static class CarLookupQueries
{
    public static Car FindByLicencePlate(this IEnumerable<Car> source, string licencePlate)
    {
        return source.SingleOrDefault(c => c.LicencePlate == licencePlate);
    }

    ...
}

Điều này phân tách mối quan tâm của việc truy vấn bộ sưu tập từ lớp lưu trữ những chiếc xe.


Theo cách tiếp cận này, tôi thậm chí có thể loại bỏ ClassCollectionngay cả các phương thức thêm, xóa, cập nhật, bằng cách thêm một phương thức mới CarCRUDsẽ gói gọn tất cả các phương thức này ...
Luis

@Luis Tôi không khuyến nghị CarCRUDtiện ích mở rộng vì việc thực thi việc sử dụng nó sẽ khó khăn, lợi thế của việc đưa logic thô tùy chỉnh vào lớp bộ sưu tập là không có cách nào để bỏ qua nó. Ngoài ra, bạn có thể không thực sự quan tâm đến logic Tìm kiếm trong cụm lõi trong đó Carvv được khai báo, đó có thể là hoạt động chỉ dành cho giao diện người dùng.
Trevor Pilley

Giống như một ghi chú từ MSDN: "Chúng tôi không khuyên bạn nên sử dụng lớp CollectionBase để phát triển mới. Thay vào đó, chúng tôi khuyên bạn nên sử dụng lớp Bộ sưu tập <T> chung." - docs.microsoft.com/en-us/dotnet/api/ Kẻ
Ryan

9

Không. Việc tạo ra các XXXCollectionlớp khá nhiều lỗi thời với sự ra đời của các thế hệ trong .NET 2.0. Trên thực tế, có Cast<T>()phần mở rộng LINQ tiện lợi mà mọi người sử dụng ngày nay để đưa các công cụ ra khỏi các định dạng tùy chỉnh đó.


1
Điều gì về các phương pháp tùy chỉnh chúng ta có thể có bên trong đó ClassCollection? nó là một thực hành tốt để đặt chúng trên chính Class?
Luis

3
Tôi tin rằng nằm trong câu thần chú phát triển phần mềm "nó phụ thuộc". Nếu bạn đang nói về, ví dụ, FindCarByModelphương thức của bạn , điều đó có ý nghĩa như một phương thức trên kho lưu trữ của bạn, thì nó hơi phức tạp hơn một Carbộ sưu tập.
Jesse C. Choper

2

Thật tiện lợi khi có các phương thức hướng miền để tìm / cắt các bộ sưu tập, như trong ví dụ FindByCarModel ở trên, nhưng không cần phải dùng đến việc tạo các lớp bộ sưu tập trình bao bọc. Trong tình huống này, bây giờ tôi sẽ thường tạo một tập hợp các phương thức mở rộng.

public static class CarExtensions
{
    public static IEnumerable<Car> ByModel(this IEnumerable<Car> cars, string model)
    {
        return cars.Where(car => car.Model == model);
    }
}

Bạn thêm bao nhiêu bộ lọc hoặc phương thức tiện ích vào lớp đó tùy thích và bạn có thể sử dụng chúng ở bất cứ đâu bạn có IEnumerable<Car>, bao gồm mọi thứ ICollection<Car>, mảng Car, IList<Car>v.v.

Vì giải pháp kiên trì của chúng tôi có nhà cung cấp LINQ, tôi cũng thường xuyên tạo các phương thức lọc tương tự hoạt động và trả về IQueryable<T>, vì vậy chúng tôi cũng có thể áp dụng các hoạt động này cho kho lưu trữ.

Thành ngữ của .NET (tốt, C #) đã thay đổi rất nhiều kể từ 1.1. Duy trì các lớp bộ sưu tập tùy chỉnh là một điều khó khăn và bạn có được chút ít từ việc kế thừa từ CollectionBase<T>giải pháp phương pháp mở rộng nếu tất cả những gì bạn cần là các phương thức chọn và lọc cụ thể theo miền.


1

Tôi nghĩ rằng lý do duy nhất để tạo ra một lớp đặc biệt để giữ một bộ sưu tập các vật phẩm khác là khi bạn thêm một thứ gì đó có giá trị vào nó, một cái gì đó hơn là chỉ gói gọn / kế thừa từ một thể hiện IListhoặc một loại bộ sưu tập khác.

Ví dụ, trong trường hợp của bạn, việc thêm một chức năng sẽ trả về danh sách phụ của ô tô đỗ trên các khoảng trống chẵn / không đều ... Và thậm chí sau đó ... có thể chỉ khi nó thường được sử dụng lại, bởi vì nếu nó chỉ mất một dòng với LinQ đẹp Chức năng và chỉ được sử dụng một lần, điểm gì? HÔN !

Bây giờ, nếu bạn có kế hoạch cung cấp nhiều phương pháp sắp xếp / tìm kiếm, thì có, tôi nghĩ rằng điều này có thể hữu ích bởi vì đây là nơi chúng nên thuộc về, trong lớp sưu tập đặc biệt đó. Đây cũng là một cách tốt để "che giấu" sự phức tạp của một số truy vấn "tìm" hoặc bất cứ điều gì bạn có thể làm trong phương pháp sắp xếp / tìm kiếm.


Ngay cả tho, tôi nghĩ rằng tôi có thể đưa các phương thức đó vào chínhClass
Luis

Vâng, thực sự ... bạn có thể
Jalayn

-1

Tôi thích đi với tùy chọn sau, vì vậy bạn có thể thêm phương thức của bạn vào bộ sưu tập và sử dụng lợi thế của danh sách.

public class CarCollection:List<Car>
{
    public Car FindCarByModel(string model)
    {
        // code here
        return new Car();
    }
}

và sau đó bạn có thể sử dụng nó như C # 7.0

public class ParkingLot
{
    public CarCollection Cars { get; set; }=new CarCollection();
    //some other properties
}

Hoặc bạn có thể sử dụng nó như

public class ParkingLot
{
   public ParkingLot()
   {
      //initial set
      Cars =new CarCollection();
   }
    public CarCollection Cars { get; set; }
    //some other properties
}

- Phiên bản chung nhờ bình luận @Bryan

   public class MyCollection<T>:List<T> where T:class,new()
    {
        public T FindOrNew(Predicate<T> predicate)
        {
            // code here
            return Find(predicate)?? new T();
        }
       //Other Common methods
     }

và sau đó bạn có thể sử dụng nó

public class ParkingLot
{
    public MyCollection<Car> Cars { get; set; }=new MyCollection<Car>();
    public MyCollection<Motor> Motors{ get; set; }=new MyCollection<Motor>();
    public MyCollection<Bike> Bikes{ get; set; }=new MyCollection<Bike>();
    //some other properties
}

không được thừa kế từ Danh sách <T>
Bryan Boettcher

@Bryan, bạn đúng câu hỏi không dành cho bộ sưu tập chung chung. Tôi sẽ sửa đổi câu trả lời của tôi cho bộ sưu tập chung chung.
Waleed AK

1
@WaleedAK bạn vẫn làm điều đó - không kế thừa từ Danh sách <T>: stackoverflow.com/questions/21692193/why-not-inherit-from-listt
Bryan Boettcher

@Bryan: nếu bạn đọc liên kết của mình Khi nào thì chấp nhận được? Khi bạn đang xây dựng một cơ chế mở rộng cơ chế Danh sách <T>. , Vì vậy, nó sẽ ổn miễn là không có thuộc tính bổ sung
Waleed AK
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.