เป็นวิธีปฏิบัติที่ดีในการสร้าง ClassCollection ของคลาสอื่นหรือไม่?


35

ให้บอกว่าฉันมีCarคลาส:

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

ให้บอกว่าเรากำลังสร้างระบบเกี่ยวกับที่จอดรถฉันจะใช้Carคลาสจำนวนมากดังนั้นเราจึงสร้างCarCollectionคลาสมันอาจมีวิธีการโฆษณาสองสามอย่างเช่นFindCarByModel:

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

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

ถ้าฉันกำลังเรียนParkingLotการฝึกที่ดีที่สุดคืออะไร?

ตัวเลือกที่ 1:

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

ตัวเลือก # 2:

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

มันเป็นแนวปฏิบัติที่ดีในการสร้างสิ่งClassCollectionอื่นClassหรือไม่?


คุณเห็นประโยชน์อะไรในการส่งผ่านCarCollectionสิ่งList<Car>รอบตัว? โดยเฉพาะอย่างยิ่งเมื่อ CarCollection ไม่ขยายคลาสของรายการสำรองหรือแม้แต่ใช้อินเทอร์เฟซการรวบรวม (ฉันแน่ใจว่า C # มีสิ่งที่คล้ายกัน)

<T> List แล้วดำเนินการ <T> IList, ICollection <T> IList, ICollection, IReadOnlyList <T> IReadOnlyCollection <T> IEnumerable <T> และ IEnumerable ... นอกจากนี้ฉันสามารถใช้ Linq ...
หลุยส์

แต่public class CarCollectionไม่ได้ใช้ IList หรือ ICollection เป็นต้น ... ดังนั้นคุณไม่สามารถส่งผ่านไปยังสิ่งที่ใช้ได้กับรายการ มันอ้างว่าเป็นส่วนหนึ่งของชื่อว่าเป็นคอลเลกชัน แต่ไม่ได้ใช้วิธีการใด ๆ

1
เป็นคำถามอายุ 6 ปีฉันเห็นไม่มีใครได้กล่าวว่านี่เป็นเรื่องธรรมดาใน DDD คอลเลกชันใด ๆ ที่ควรจะแยกออกเป็นชุดที่กำหนดเอง ตัวอย่างเช่นคุณต้องการคำนวณมูลค่าของกลุ่มรถยนต์ คุณจะวางตรรกะนั้นไว้ที่ไหน ในการบริการหรือไม่? หรือใน DDD คุณจะต้องCarColectionมีTotalTradeValueคุณสมบัติกับมัน DDD ไม่ใช่วิธีเดียวในการออกแบบระบบเพียงชี้ให้เห็นว่าเป็นตัวเลือก
Storm Muller

1
ว่าสิ่งเหล่านี้รวมรากจริงตามที่แนะนำใน DDD สิ่งเดียวที่เป็น DDD อาจไม่แนะนำให้คุณเรียกมันว่าคอลเลกชันรถ ค่อนข้างใช้ชื่อบางอย่างเช่นที่จอดรถหรือพื้นที่ทำงาน ฯลฯ
ชื่อรหัสแจ็ค

คำตอบ:


40

ก่อนหน้า generics ใน. NET มันเป็นเรื่องธรรมดาที่จะสร้างคอลเลกชัน 'พิมพ์' ดังนั้นคุณจะมีclass CarCollectionฯลฯ สำหรับทุกประเภทที่คุณต้องการจัดกลุ่ม ใน. NET 2.0 ด้วยการแนะนำ Generics คลาสใหม่List<T>ได้รับการแนะนำซึ่งช่วยให้คุณไม่ต้องสร้างCarCollectionฯลฯ ตามที่คุณสามารถสร้างList<Car>ได้

ส่วนใหญ่คุณจะพบว่าList<T>เพียงพอสำหรับวัตถุประสงค์ของคุณอย่างไรก็ตามอาจมีบางครั้งที่คุณต้องการมีพฤติกรรมเฉพาะในคอลเลกชันของคุณหากคุณเชื่อว่าเป็นกรณีนี้คุณมีตัวเลือกสองทาง:

  • สร้างชั้นที่ห่อหุ้มList<T>เช่นpublic class CarCollection { private List<Car> cars = new List<Car>(); public void Add(Car car) { this.cars.Add(car); }}
  • สร้างคอลเลกชันที่กำหนดเอง public class CarCollection : CollectionBase<Car> {}

หากคุณใช้วิธี encapsulation คุณควรเปิดเผยตัวระบุอย่างน้อยดังนั้นคุณจะประกาศดังต่อไปนี้:

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

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

หากไม่ทำเช่นนั้นคุณจะไม่สามารถทำforeachคอลเล็กชันได้

เหตุผลบางอย่างที่คุณอาจต้องการสร้างคอลเลกชันที่กำหนดเองคือ:

  • คุณไม่ต้องการเปิดเผยวิธีการทั้งหมดในIList<T>หรือICollection<T>
  • คุณต้องการดำเนินการเพิ่มเติมเมื่อเพิ่มหรือลบรายการออกจากคอลเลกชัน

เป็นการปฏิบัติที่ดีหรือไม่? ดีขึ้นอยู่กับว่าทำไมคุณถึงทำเช่นนั้นหากเป็นตัวอย่างหนึ่งในเหตุผลที่ฉันได้ระบุไว้ข้างต้นใช่แล้ว

Microsoft ทำค่อนข้างบ่อยนี่เป็นตัวอย่างล่าสุด:

สำหรับFindByวิธีการของคุณฉันจะถูกล่อลวงให้ใส่พวกเขาในวิธีการขยายเพื่อให้พวกเขาสามารถใช้กับคอลเลกชันที่มีรถยนต์:

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

    ...
}

สิ่งนี้แยกความกังวลของการสอบถามคอลเลกชันจากคลาสที่เก็บรถยนต์


ต่อไปนี้วิธีการนี้ผมยังสามารถยกเลิกClassCollectionแม้กระทั่งสำหรับการเพิ่มลบวิธีการปรับปรุงโดยการเพิ่มใหม่CarCRUDซึ่งจะสรุปวิธีการทั้งหมดนี้ ...
หลุยส์

@Luis ฉันไม่แนะนำให้ใช้CarCRUDส่วนขยายเนื่องจากการบังคับใช้จะยากข้อได้เปรียบในการวางตรรกะ crud แบบกำหนดเองในคลาสคอลเล็กชันคือไม่มีทางที่จะข้ามมันได้ นอกจากนี้คุณอาจไม่สนใจเกี่ยวกับตรรกะการค้นหาในแอสเซมบลีคอร์ที่Carมีการประกาศ ฯลฯ ซึ่งอาจเป็นกิจกรรม UI เท่านั้น
Trevor Pilley

เช่นเดียวกับข้อความจาก MSDN: "เราไม่แนะนำให้คุณใช้คลาส CollectionBase สำหรับการพัฒนาใหม่ แต่เราขอแนะนำให้คุณใช้คลาส Collection <T> ทั่วไป" - docs.microsoft.com/en-us/dotnet/api/…
Ryan

9

ไม่การสร้างXXXCollectionชั้นเรียนค่อนข้างล้าสมัยไปพร้อมกับการกำเนิดของ generics ใน. NET 2.0 อันที่จริงมีCast<T>()ส่วนขยาย LINQ ที่ดีที่ผู้คนใช้เวลาเหล่านี้เพื่อดึงข้อมูลจากรูปแบบที่กำหนดเองเหล่านั้น


1
แล้วเราจะมีวิธีการที่กำหนดเองได้ClassCollectionอย่างไร เป็นวิธีที่ดีหรือไม่ที่จะนำมาเป็นหลักClass?
ลูอิส

3
ฉันเชื่อว่าตกอยู่ภายใต้มนต์การพัฒนาซอฟต์แวร์ของ "มันขึ้นอยู่กับ" หากคุณกำลังพูดถึงตัวอย่างเช่นFindCarByModelวิธีการของคุณที่มีเหตุผลในฐานะที่เป็นวิธีการในพื้นที่เก็บข้อมูลของคุณซึ่งมีความซับซ้อนมากกว่าเพียงแค่Carคอลเลกชัน
Jesse C. Slicer

2

บ่อยครั้งมีประโยชน์มากที่จะมีวิธีการที่เน้นโดเมนเพื่อค้นหา / รวบรวมคอลเล็กชันดังตัวอย่างใน FindByCarModel ด้านบน แต่ไม่จำเป็นต้องหันไปใช้การสร้างคลาสคอลเลกชัน wrapper ในสถานการณ์นี้ตอนนี้ฉันจะสร้างชุดของวิธีการขยาย

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

คุณสามารถเพิ่มเป็นตัวกรองหรือยูทิลิตี้หลายวิธีการที่จะเรียนเท่าที่คุณต้องการและคุณสามารถใช้พวกเขาทุกที่ที่มีIEnumerable<Car>ซึ่งรวมถึงอะไรICollection<Car>อาร์เรย์ของCar, IList<Car>ฯลฯ

เนื่องจากโซลูชันการคงอยู่ของเรามีผู้ให้บริการ LINQ ฉันจึงมักจะสร้างวิธีการตัวกรองที่คล้ายกันซึ่งใช้งานและส่งคืนIQueryable<T>ดังนั้นเราจึงสามารถใช้การดำเนินการเหล่านี้กับที่เก็บได้เช่นกัน

สำนวนของ. NET (ดี C #) มีการเปลี่ยนแปลงมากตั้งแต่ 1.1 การรักษาคลาสคอลเลกชันที่กำหนดเองเป็นสิ่งที่เจ็บปวดและคุณจะได้รับเล็กน้อยจากการสืบทอดจากCollectionBase<T>สิ่งที่คุณไม่ได้รับด้วยวิธีการส่วนขยายหากทั้งหมดที่คุณต้องการคือตัวกรองเฉพาะโดเมนและวิธีการเลือก


1

ฉันคิดว่าเหตุผลเดียวที่สร้างคลาสพิเศษสำหรับเก็บรายการอื่น ๆ ควรจะเป็นเมื่อคุณเพิ่มสิ่งที่มีค่าลงไปสิ่งที่มากกว่าเพียงแค่แค็ปซูล / รับมรดกจากอินสแตนซ์ของIListหรือคอลเล็กชันประเภทอื่น

ตัวอย่างเช่นในกรณีของคุณการเพิ่มฟังก์ชั่นที่จะส่งคืนรายการย่อยของรถยนต์ที่จอดอยู่บนพื้นที่จำนวนมาก / ไม่เท่ากัน ... และแม้กระทั่ง ... อาจจะถ้ามันถูกนำมาใช้บ่อยเพราะถ้าใช้เพียงบรรทัดเดียวด้วย LinQ ฟังก์ชั่นและใช้เพียงครั้งเดียวสิ่งที่เป็นประเด็น? จูบ !

ตอนนี้ถ้าคุณวางแผนที่จะนำเสนอวิธีการเรียงลำดับ / ค้นหาจำนวนมากใช่ฉันคิดว่านี่อาจเป็นประโยชน์เพราะนี่คือที่ที่พวกเขาควรอยู่ในคลาสคอลเลกชันพิเศษนั้น นี่เป็นวิธีที่ดีในการ "ซ่อน" ความซับซ้อนของข้อความค้นหา "find" หรือสิ่งที่คุณสามารถทำได้ในวิธีการเรียงลำดับ / ค้นหา


แม้สรรพสินค้าฉันคิดว่าฉันสามารถรวมวิธีการเหล่านั้นไว้ในหลักได้Class
ลูอิส

ใช่แน่นอน ... คุณสามารถทำได้
Jalayn

-1

ฉันชอบที่จะใช้ตัวเลือกต่อไปนี้เพื่อให้คุณสามารถเพิ่มวิธีการในการรวบรวมและใช้ประโยชน์จากรายการ

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

และจากนั้นคุณสามารถใช้มันเช่น C # 7.0

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

หรือคุณสามารถใช้มันเหมือน

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

- รุ่นทั่วไปขอบคุณที่แสดงความคิดเห็น @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
     }

และจากนั้นคุณสามารถใช้มัน

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
}

ไม่สืบทอดจากรายการ <T>
Bryan Boettcher

@Bryan คุณถูกคำถามไม่ใช่สำหรับคอลเลกชันทั่วไป ฉันจะแก้ไขคำตอบของฉันสำหรับคอลเล็กชันทั่วไป
Waleed AK

1
@WaleedAK คุณยังทำอยู่ - อย่าสืบทอดจากรายการ <T>: stackoverflow.com/questions/21692193/why-not-inherit-from-listt
Bryan Boettcher

@Bryan: ถ้าคุณอ่านลิงค์ของคุณเมื่อไหร่จะยอมรับได้? เมื่อคุณสร้างกลไกที่ขยายกลไกรายการ <T> ดังนั้นมันจะดีถ้าไม่มีคุณสมบัติเพิ่มเติม
Waleed AK
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.