【问题标题】:Good class design by example通过示例进行良好的课堂设计
【发布时间】:2011-09-01 19:01:38
【问题描述】:

我正在尝试找出设计一个将其属性保存在数据库中的类的最佳方法。让我们以Person 为例。要创建一个新人并将其放入数据库中,我希望 DateOfBirth 属性是可选的(即在数据库中可以为 NULL)。

这是我的示例代码:

namespace BusinessLayer
{
    class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime DateOfBirth { get; set; }
    }
}

我不确定这些字段是否应该公开。我应该这样做吗:

class Program
{
    static void Main(string[] args)
    {
        Person person1 = new Person("Kate","Middleton",null);
    }
}

或者像这样:

class Program
{
    static void Main(string[] args)
    {
        Person person1 = new Person();
        person1.FirstName = "Kate";
        person1.LastName = "Middleton";
    }
}

我也想知道我应该如何处理类的可选属性。填充字段后,如何将它们保存到数据库中?我有一个 DatabaseComponenet 类来保存信息。保存到数据库时如何处理可选?

那么,我会做这样的事情吗:

public int Save()
{
    int personId;
    personId = DatabaseComponent.InsertPerson(FirstName, LastName, DateOfBirth);
    return personId;
}

感谢您的帮助!一些关于良好类设计的有用 URL 也将不胜感激。

【问题讨论】:

    标签: c# c#-4.0


    【解决方案1】:

    使用 C# 3.0 类初始化器,我不再需要提供一个允许我初始化所有属性的构造器:

    var person1 = new Person
    {
        FirstName = "Kate";
        LastName = "Middleton";
    };
    

    Save 方法而言,我通常将它们放在一个单独的存储库类中:

    public int Save(Person person) 
    {
        ...
    }
    

    然后当我需要拯救一个人时,我会这样做:

    var person1 = new Person
    {
        FirstName = "Kate";
        LastName = "Middleton";
    };
    var id = new PersonsRepository().Save(person1);
    

    【讨论】:

    • 拥有构造函数(然后没有默认构造函数)的优点是必须初始化属性。
    • 我同意,并且在实体框架代码优先方法中使用了几乎相同的原则,也用于 wcf 上的数据合同。
    • @Lou Franco,我让我的应用程序/数据库的验证层关心必须初始化的内容。
    • @Darin,构造函数很好!我同意卢的观点。我与一个遵循这一原则的开发人员一起工作,但我们有很多验证错误,因为开发人员只是忘记了初始化属性。构造函数可以解决这个问题。
    • Ditto Lou Franco 评论。构造函数是确保创建具有有效数据的有效对象的更好选择。但我确实使用了那个初始化器语法。我喜欢用它来创建静态对象、嵌套类对象以及我在内部创建复合对象的地方。然而,在手边,以这种方式初始化子类对象可能会违反Liskov Substitution Principle
    【解决方案2】:

    这应该通过使用业务规则来检查。

    我的意思是,如果你想要一个非常可重用的业务模型,业务对象应该在不同领域的其他地方重用,这可能意味着在某些业务的状态“X”中相同的类“A”可能很好,但是在另一种情况下,相同的“A”类,在“Y”状态下也可以。

    有一个很好的设计模式允许您实现称为规范的业务验证器:

    这可以通过多种方式实现,但最紧凑的方式之一是使用 lambda 表达式构建规则。

    例如:

    someAInstance => someAInstance.Name != null && someAInstance.Age > 30
    

    另一种方法是使用现有的对象验证库,例如 NHibernate Validator,它可以在没有 NHibernate 的情况下独立使用,并允许您将属性放入类的属性中,例如 [NotNull][NotNullNotEmpty] 和更复杂的规则,您可以可以使用内置的,也可以构建自己的。

    阅读这篇文章了解更多信息(您会在其中找到开箱即用的验证规则列表)

    请注意,NH Validator 最重要的优点之一是它可以用于任何层,不仅是数据层或业务层,而且您可以在没有 NHibernate 的情况下使用它,因此您拥有一个轻量级、易于使用的使用和多层对象验证器。

    【讨论】:

      【解决方案3】:

      仅当某些字段是必填时才使用构造函数,因为这是确保指定这些字段的有效方法。

      我不确定这些字段是否应该公开

      字段通常意味着成员变量,并且它们应该始终是私有的。至于属性,我会坚持使用 get/set 来获取数据库对象。

      我也想知道我应该如何处理类的可选属性。填充字段后,如何将它们保存到数据库中?

      将内容保存到数据库是完全不同的故事。我不会尝试发明自己的层,而是使用现有的层。有一整套不同的 ORM:从非常简单到功能齐全。

      查看PetaPoco 以获得轻量级替代方案或查看nHibernate 以获得更多功能完整的替代方案。

      验证

      确保正确指定必填字段并获得有效值的一种常见方法是使用验证框架。 .net 中内置了一个名为DataAnnotations。谷歌一下,看看一些例子。

      【讨论】:

        【解决方案4】:

        首先,我将两个不同的公共构造函数放入 Person:

        namespace BusinessLayer
        {
            class Person
            {
                public Person(string firstName, string lastName): this(firstName, lastName, DateTime.Now)
                {}
        
                public Person(string firstName, string lastName, DateTime birthDate)
                {
                    FirstName = firstName;
                    LastName = lastName;
                    DateOfBirth = birthDate;
                }
        
                public string FirstName { get; set; }
                public string LastName { get; set; }
                public DateTime DateOfBirth { get; set; }
            }
        }
        

        这允许你写两个

        var p = new Person("Marilyin", "Manson");
        var p2 = new Person("Alice", "Cooper", new DateTime(...));
        

        var p = new Person { FirstName="Marilyn", LastName="Manson" };
        

        我不明白为什么你应该只限于一种形式。

        至于 DatabaseComponent,我强烈建议您编写一个方法来保存 Person 而不是您隐式声明的签名。

        那是因为,如果有一天改变 Person 的定义方式,您可能不得不更改调用 Save() 方法的每个点的代码。通过只保存一个 Person,您只需更改 Save() 实现。

        顺便说一句,你不打算使用 ORM 吗?

        【讨论】:

        • 谢谢 我在this:The best overloaded method match for 'BusinessLayer.Person.Person(string, string, System.DateTime)' has some invalid arguments之后得到一个错误
        • @Mark 是的,我忘记了 DateTime 不是可以为空的类型,您应该使用 DateTime?/Nullable<DateTime> 或为其分配一个有意义的值。
        • emmm 很抱歉,为什么你要在课程结束时声明字段??
        • @AminM 这些是属性,而不是字段。不管怎样,没有什么特别的原因,我可以把它们放在开头,我真的不在乎这样一个小例子
        • @Simone Tnx 但我认为这是 OOP 设计的实践
        猜你喜欢
        • 2015-10-07
        • 2012-11-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多