日常生活的交流与学习

首页 新随笔 联系 管理

封装前和封装后的实体类设计

如何封装

封装前的例子

假设我们有一个用户实体类 User,在封装前,可能看起来像这样:

public class User
{
    public int Id { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
    public List<Role> Roles { get; set; }

    public User()
    {
        Roles = new List<Role>();
    }
}

在这个例子中,所有的属性都可以被外部代码直接修改,没有任何限制或验证。

封装后的例子

在进行了适当的封装后,User 类可能会变成如下所示:

public class User : Entity<int>
{
    public string UserName { get; protected set; }
    public string Email { get; protected set; }
    public List<Role> Roles { get; protected set; }

    protected User()
    {
        // 无参数构造函数,一般不对外可见
        Roles = new List<Role>();
    }

    public User(int id, string userName, string email)
        : this(id)
    {
        UserName = userName ?? throw new ArgumentNullException(nameof(userName));
        Email = email ?? throw new ArgumentNullException(nameof(email));
    }

    public User(int id)
    {
        Id = id;
        Roles = new List<Role>();
    }

    public void SetUserName(string userName)
    {
        UserName = userName ?? throw new ArgumentNullException(nameof(userName));
    }

    public void SetEmail(string email)
    {
        Email = email ?? throw new ArgumentNullException(nameof(email));
    }

    public void AddRole(Role role)
    {
        if (role == null)
            throw new ArgumentNullException(nameof(role));

        if (!Roles.Any(r => r.Id == role.Id))
            Roles.Add(role);
    }

    public void RemoveRole(Role role)
    {
        if (role == null)
            throw new ArgumentNullException(nameof(role));

        Roles.RemoveAll(r => r.Id == role.Id);
    }
}

在这个封装后的例子中,我们做了以下改动:

  1. 构造函数:

    • 无参数构造函数被设为 protected,不允许外部直接创建实例。
    • 有参数构造函数检查参数有效性,并初始化属性。
    • 初始化了 Roles 列表。
  2. 属性访问器:

    • UserNameEmail 的 setter 被设为 protected,不允许外部直接修改。
    • 提供了 SetUserNameSetEmail 方法,用来安全地设置这些属性,并进行有效性检查。
  3. 集合操作:

    • Roles 的 setter 被设为 protected,不允许外部直接修改。
    • 提供了 AddRoleRemoveRole 方法来安全地添加和删除角色。

通过这种方式,我们可以更好地控制实体的状态,并确保实体的一致性和有效性。

构造函数

protected User()
{
    // 无参数构造函数,一般不对外可见
    Roles = new List<Role>();
}
  1. 这是一个无参数的构造函数,它被声明为 protected,意味着只有该类本身或其派生类可以调用它。
  2. 在构造函数体内,初始化了一个 Roles 列表。这是一个包含 Role 类型对象的列表,用于存储用户的各个角色。
  3. 通常情况下,无参数构造函数并不推荐对外开放,因为这可能导致实体对象在未完全初始化的情况下就被创建。这里的目的是为了让派生类可以使用它,或者在某些框架中需要无参数构造函数的情况。
public User(int id, string userName, string email)
    : this(id)
{
    UserName = userName ?? throw new ArgumentNullException(nameof(userName));
    Email = email ?? throw new ArgumentNullException(nameof(email));
}
  1. 这是一个带有三个参数的构造函数:int idstring userNamestring email
  2. 使用 : this(id) 表示这个构造函数会先调用另一个带有 int id 参数的构造函数来初始化部分成员变量。
  3. 在构造函数体内,通过 ?? 运算符检查 userNameemail 是否为 null。如果是 null,则抛出 ArgumentNullException,并指明哪个参数未被赋值。
  4. 如果参数不是 null,则设置 UserNameEmail 属性。
public User(int id)
{
    Id = id;
    Roles = new List<Role>();
}
  1. 这是一个带有一个参数 int id 的构造函数。
  2. 在构造函数体内,初始化了 Id 属性,并创建一个新的 Roles 列表。

总结

这些构造函数的设计原则如下:

  1. 无参数构造函数 (protected User()): 一般用于派生类或某些框架的需求,在这里主要是为了派生类能够继承此类时调用。
  2. 带有必要参数的构造函数 (public User(int id, string userName, string email)): 这个构造函数用于创建一个完整的 User 对象,其中包含了必需的参数。它还检查了参数的有效性,确保不会创建无效的用户对象。
  3. 带有部分必要参数的构造函数 (public User(int id)): 这个构造函数用于创建一个具有唯一标识符 idUser 对象,但是不包含所有必需的信息。通常用于某些特定场景下的初始化。

通过这样的构造函数设计,我们可以确保在创建 User 对象时,对象的属性已经被适当地初始化,并且遵循了业务规则。这种做法有助于提高代码的质量和可维护性。

posted on 2024-08-18 15:44  lazycookie  阅读(5)  评论(0编辑  收藏  举报