【问题标题】:Class Constructor and Get Design类构造函数和获取设计
【发布时间】:2015-07-09 19:22:47
【问题描述】:

在设计类和构造函数时,我有一个关于获取记录的问题。

举个例子,如果我有汽车课

public Car
{
    ...
}

我可以选择创建一个可以获取特定汽车实例的构造函数,例如:

public Car(int carID)
{
    //Get car from database
}

或者我可以编写一个公共方法来获取汽车:

public Car GetCarByID(int carID)
{
    //Get car from database
}

是否有获取类实例的首选(最佳实践)方法?代码似乎读起来更干净,可以获取对象的实例:

Car MyCar = new Car(5);

对比

Car MyCar = new Car();
MyCar.GetCarByID(5);

【问题讨论】:

  • 在这种情况下,我将有一个名为 car 的类来处理所有基本信息。 (如身份证)。然后我会有一个名为 CarCollection 的类,其中包含集合中的汽车。在这个类中,我将使用 GetCarById 方法。在该方法中,我将搜索汽车集合并返回具有该 ID 的汽车。这个问题是非常基于意见的,因为它不是这里的主题。
  • 关注点分离,如果你的car类确实是一个包含car信息的类,你应该使用数据访问层来构造car对象,不需要@ 987654329@了解数据库。
  • 为什么汽车应该知道如何从数据库中获取自己?您的两个提案都不好,违反了 SRP。

标签: c# class constructor data-access


【解决方案1】:

我建议将您的数据库访问与 Car 等域模型类分开。

这可以通过将所有数据库访问检索操作放在它们自己的一组类中来完成。这些被称为存储库。制作这些存储库有不同的方法。这取决于您的数据如何存储在数据库中以及您是否使用实体框架。有些人更喜欢通用存储库,而另一些人则根本不喜欢这种通用方法。但最重要的是,存储库负责访问数据库以进行对象检索和存储。它不应该由域对象(即 Car 本身)来完成,因为它应该只有属于 Car 的操作以及使其成为 Car 对象的原因。将 Car 存储在数据库中并不是 Car 应该知道的操作。

可以在此处找到使用存储库模式的示例: http://www.codeproject.com/Articles/688929/Repository-Pattern-and-Unit-of

MSDN 上还有一篇关于存储库模式的好文章: https://msdn.microsoft.com/en-us/library/ff649690.aspx?f=255&MSPPError=-2147217396

底线是使用存储库访问数据库。它用于从数据库加载和存储数据,并提供额外的抽象层。如果需要,您可以为每个存储库创建一个接口,这样您也可以进行单元测试。

比在你的类中创建不属于那里的方法要好得多。

【讨论】:

  • 感谢您的解释。虽然这带来了另一个问题,如果我有一个 DriveCar() 方法需要从数据库中访问汽车的最大速度,我认为 DriveCar() 方法应该属于类定义,然后它会调用汽车存储库类来获得最大速度?还是我想错了?
  • 汽车存储库检索属于汽车的数据的那一刻,它应该检索有关汽车的所有重要方面以及使汽车成为汽车的原因。最大速度显然是汽车的一个属性,所以是的,当您从 Db 加载汽车时,应该直接检索它。通过进行域驱动设计并仅使用聚合根作为入口点,您可以在这个方向上走得更远。但这是一个不同的话题。 methodsandtools.com/archive/archive.php?id=97p2
【解决方案2】:

正如我在评论中所说,我会这样做来管理汽车并允许通过 id 或其他任何您想要加载的方式加载汽车。

public class Car
{
    public int Id { get; set; }
    public Car() { }
    public Car(int id) { Id = id; }
}

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

    public void AddCar(Car car) { Cars.Add(car); }

    public Car GetById(int id) { return Cars.Single(x => x.Id == id); }

    public CarCollection(List<Car> cars) { Cars = cars; }
    public CarCollection(Car car) { Cars = new List<Car>() { car }; }
}

【讨论】:

  • 我喜欢这个例子,但是实现似乎需要很多开销。例如,要通过 Id 获取汽车,我首先必须实例化一个 Car 对象,然后实例化一个 CarCollection 对象来填充 Car 对象?其他一些示例使用静态类,但从我的阅读中听起来静态类不应该以这种方式使用?
  • 我不明白这如何使用大量开销,因为无论如何您都需要将所有汽车存储在列表/数组/字典/其他数据结构中。该集合允许轻松访问您想要的汽车,而无需编写静态方法。在这些静态方法中,您必须将一组汽车传递给它们,以便它们能够迭代汽车并选择所需的汽车。上面的类已经处理了。例如,如果您曾经使用过 DataGridView,您还会注意到它类似于 RowCollection 和 ColumnCollection 类。
【解决方案3】:

一个简单的解决方案是使用static method,如下所示:

public static Car GetById(int carId)
{
    ... Load car and return it ...
}

那么你可以这样称呼它:

Car myCar = Car.GetById(18); 

【讨论】:

  • 一辆汽车绝对不应该使用这些方法进行扩展。它违反了 SRP,并且很难进行单元测试。
  • 哦,这真的取决于。对于一个小型项目,添加这样一个静态方法可以节省您的时间,而不会花费您任何费用。这就是为什么我说这是一种解决方案,而且很简单。
【解决方案4】:

为了解决 cmets 关于关注点分离的问题,我建议如下:

public class Car
{
    public string Make {get; set;}
    public string Model {get; set;}
    // additional properties here.
}

public static class CarFactory
{
    public static Car CreateCar(string modelId)
    {
        Car car = new Car();
        // Load the car via whatever mechanism you wish;
        // e.g., web service, database, etc.
        return car;
}

Car fordFairlane = CarFactory.CreateCar("57fordFairlane");

这将创建汽车对象的代码与汽车对象本身分开,并允许您更改汽车的创建方式,而无需接触汽车类。

【讨论】:

  • 这并没有提供一种简单的方法来通过 id 加载汽车。这只是创建了一辆带有 id 的新车。
  • 加载代码并不是很重要,所以我没有放很多无关的数据库代码。该示例的重要之处在于它强调了如何将创建对象的代码保留在对象的类代码之外。因此调用构造函数后的 cmets。
  • 不,评论清楚地表明您可以从数据库中加载汽车。向这个所谓的“工厂”添加一种存储方式,您所拥有的是一个存储库。
猜你喜欢
  • 2013-03-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-21
  • 1970-01-01
  • 2011-10-09
  • 2017-09-14
  • 2017-05-28
相关资源
最近更新 更多