Apakah ini praktik yang baik untuk membuat ClassCollection dari Kelas lain?

35

Ayo bilang aku ada Carkelas:

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

Katakanlah kita membuat sistem tentang tempat parkir, saya akan menggunakan banyak Carkelas, jadi kami membuat CarCollectionkelas, mungkin ada beberapa metode tambahan seperti FindCarByModel:

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

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

Jika saya membuat kelas ParkingLot, apa praktik terbaik?

Pilihan 1:

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

Pilihan 2:

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

Apakah ini merupakan praktik yang baik untuk menciptakan yang ClassCollectionlain Class?

Luis
sumber
Apa manfaat yang Anda rasakan dalam melewati sebuah CarCollectiondaripada List<Car>sekitar? Terutama mengingat bahwa CarCollection tidak memperpanjang kelas Daftar backing, atau bahkan mengimplementasikan antarmuka Koleksi (saya yakin bahwa C # memiliki hal serupa).
Daftar <T> sudah mengimplementasikan IList <T>, ICollection <T>, IList, ICollection, IReadOnlyList <T>, IReadOnlyCollection <T>, IEnumerable <T> dan IEnumerable ... Selain itu, saya bisa menggunakan Linq ...
Luis
Tetapi public class CarCollectiontidak menerapkan IList atau ICollection, dll ... sehingga Anda tidak dapat meneruskannya ke sesuatu yang ok dengan daftar. Ia mengklaim sebagai bagian dari namanya bahwa itu adalah koleksi, tetapi tidak menerapkan metode-metode tersebut.
1
menjadi pertanyaan berusia 6 tahun saya tidak melihat ada yang menyebutkan bahwa ini adalah praktik umum dalam DDD. Setiap koleksi harus diabstraksi menjadi koleksi khusus. Misalnya katakanlah Anda ingin menghitung nilai sekelompok mobil. Di mana Anda akan meletakkan logika itu? dalam suatu layanan? Atau di DDD Anda akan memiliki CarColectiondengan TotalTradeValueproperti di atasnya. DDD bukan satu-satunya cara untuk merancang sistem, hanya menunjukkannya sebagai opsi.
Storm Muller
1
Persis, Hal-hal ini sebenarnya adalah akar agregat seperti yang disarankan dalam DDD, Hanya masalahnya DDD mungkin tidak akan merekomendasikan Anda untuk menyebutnya koleksi mobil. Alih-alih menggunakan beberapa nama seperti Tempat Parkir, atau Area Kerja dll.
Nama Kode Jack

Jawaban:

40

Sebelum obat generik di .NET, sudah menjadi praktik umum untuk membuat koleksi 'diketik' sehingga Anda akan memiliki class CarCollectiondll untuk setiap jenis yang Anda butuhkan untuk dikelompokkan. Dalam. NET 2.0 dengan pengenalan Generics, sebuah kelas baru List<T>diperkenalkan yang menghemat Anda harus membuat CarCollectiondll seperti yang Anda bisa buat List<Car>.

Sebagian besar waktu, Anda akan menemukan bahwa List<T>itu cukup untuk tujuan Anda, namun mungkin ada saat-saat Anda ingin memiliki perilaku tertentu dalam koleksi Anda, jika Anda yakin ini yang terjadi, Anda memiliki beberapa pilihan:

  • Buat kelas yang merangkum List<T>misalnyapublic class CarCollection { private List<Car> cars = new List<Car>(); public void Add(Car car) { this.cars.Add(car); }}
  • Buat koleksi khusus public class CarCollection : CollectionBase<Car> {}

Jika Anda menggunakan pendekatan enkapsulasi, Anda harus setidaknya mengekspos pencacah sehingga Anda akan mendeklarasikannya sebagai berikut:

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

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

Tanpa melakukan itu, Anda tidak dapat melakukan foreachover koleksi.

Beberapa alasan Anda mungkin ingin membuat koleksi khusus adalah:

  • Anda tidak ingin mengekspos sepenuhnya semua metode di IList<T>atauICollection<T>
  • Anda ingin melakukan tindakan tambahan saat menambah atau menghapus item dari koleksi

Apakah ini praktik yang baik? baik itu tergantung pada mengapa Anda melakukannya, jika itu misalnya salah satu alasan saya sebutkan di atas maka ya.

Microsoft melakukannya dengan cukup teratur, berikut adalah beberapa contoh yang cukup baru:

Adapun FindBymetode Anda , saya akan tergoda untuk memasukkannya dalam metode ekstensi sehingga mereka dapat digunakan terhadap koleksi yang berisi mobil:

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

    ...
}

Ini memisahkan kekhawatiran untuk menanyakan koleksi dari kelas yang menyimpan mobil.

Trevor Pilley
sumber
Mengikuti pendekatan ini, saya bahkan dapat mengabaikan ClassCollectionbahkan untuk menambah, menghapus, memperbarui metode, dengan menambahkan yang baru CarCRUDyang akan merangkum semua metode ini ...
Luis
@Luis Saya tidak akan merekomendasikan CarCRUDekstensi karena menegakkan penggunaannya akan sulit, keuntungan untuk menempatkan logika crud kustom di kelas koleksi adalah bahwa tidak ada cara untuk memotongnya. Selain itu, Anda mungkin tidak benar-benar peduli tentang logika Temukan di perakitan inti tempat Cardll dideklarasikan, yang mungkin hanya aktivitas UI.
Trevor Pilley
Sama seperti catatan dari MSDN: "Kami tidak menyarankan Anda menggunakan kelas CollectionBase untuk pengembangan baru. Sebaliknya, kami menyarankan Anda menggunakan kelas <T> Koleksi generik." - docs.microsoft.com/en-us/dotnet/api/…
Ryan
9

Tidak. Pembuatan XXXCollectionkelas tidak lagi sesuai gaya dengan munculnya obat generik di .NET 2.0. Bahkan, ada Cast<T>()ekstensi LINQ bagus yang digunakan orang hari ini untuk mendapatkan hal-hal dari format kustom tersebut.

Jesse C. Slicer
sumber
1
Bagaimana dengan metode khusus yang bisa kita miliki di dalamnya ClassCollection? apakah itu praktik yang baik untuk menempatkan mereka pada yang utama Class?
Luis
3
Saya percaya itu jatuh di bawah mantra pengembangan perangkat lunak "itu tergantung". Jika Anda berbicara tentang, misalnya, FindCarByModelmetode Anda , itu masuk akal sebagai metode pada repositori Anda, yang sedikit lebih kompleks daripada sekadar Carkoleksi.
Jesse C. Slicer
2

Seringkali berguna untuk memiliki metode berorientasi domain untuk menemukan / mengiris koleksi, seperti pada contoh FindByCarModel di atas, tetapi tidak perlu menggunakan resor untuk membuat kelas koleksi pembungkus. Dalam situasi ini saya sekarang biasanya akan membuat satu set metode ekstensi.

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

Anda menambahkan sebanyak mungkin metode filter atau utilitas ke kelas yang Anda inginkan, dan Anda dapat menggunakannya di mana saja IEnumerable<Car>, termasuk apa saja ICollection<Car>, array Car, IList<Car>dll.

Karena solusi kegigihan kami memiliki penyedia LINQ, saya akan sering juga membuat metode filter serupa yang beroperasi dan kembali IQueryable<T>, sehingga kami dapat menerapkan operasi ini ke repositori juga.

Ungkapan .NET (well, C #) telah banyak berubah sejak 1.1. Mempertahankan kelas koleksi khusus adalah hal yang menyusahkan, dan Anda mendapatkan sedikit dari mewarisi dari CollectionBase<T>yang tidak Anda dapatkan dengan solusi metode ekstensi jika semua yang Anda butuhkan adalah metode filter dan pemilih khusus domain.

Neil Hewitt
sumber
1

Saya pikir satu-satunya alasan untuk membuat kelas khusus untuk menyimpan koleksi item lain adalah ketika Anda menambahkan sesuatu yang bernilai padanya, sesuatu yang lebih dari sekedar merangkum / mewarisi dari instance IListatau jenis koleksi lainnya.

Sebagai contoh, dalam kasus Anda, menambahkan fungsi yang akan mengembalikan sublists mobil yang diparkir di ruang genap / tidak rata ... Dan bahkan kemudian ... mungkin hanya jika itu sering digunakan kembali, karena jika hanya membutuhkan satu baris dengan LinQ yang bagus berfungsi dan hanya digunakan sekali, apa gunanya? KISS !

Sekarang, jika Anda berencana untuk menawarkan banyak metode penyortiran / pencarian, maka ya, saya pikir ini bisa berguna karena ini adalah tempat mereka seharusnya berada, di kelas koleksi khusus. Ini juga merupakan cara yang baik untuk "menyembunyikan" kerumitan beberapa pertanyaan "temukan" atau apa pun yang dapat Anda lakukan dengan metode penyortiran / pencarian.

Jalayn
sumber
Bahkan, saya pikir saya bisa memasukkan metode-metode itu pada umumnyaClass
Luis
Ya, memang ... Anda bisa
Jalayn
-1

Saya lebih suka menggunakan opsi berikut, sehingga Anda dapat menambahkan metode Anda ke koleksi dan menggunakan keunggulan daftar.

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

dan kemudian Anda dapat menggunakannya seperti C # 7.0

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

Atau Anda bisa menggunakannya seperti

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

- Versi generik berkat komentar @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
     }

dan kemudian Anda bisa menggunakannya

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
}
Waleed AK
sumber
jangan mewarisi dari Daftar <T>
Bryan Boettcher
@Bryan, Anda benar pertanyaannya bukan untuk koleksi generik. Saya akan mengubah jawaban saya untuk koleksi generik.
Waleed AK
1
@WaleedAK Anda masih melakukannya - jangan mewarisi dari Daftar <T>: stackoverflow.com/questions/21692193/why-not-inherit-from-listt
Bryan Boettcher
@Bryan: jika Anda membaca tautan Anda Kapan itu bisa diterima? Saat Anda sedang membangun mekanisme yang memperpanjang mekanisme Daftar <T>. , Jadi itu akan baik-baik saja selama tidak ada properti tambahan
Waleed AK