如何使用面向对象编程来设计这个?

3
我有一个名为User的对象,其属性包括用户名、年龄、密码、电子邮件等。
我在我的ado.net代码中这样初始化这个对象:
private User LoadUser(SqlDataReader reader)
{
      User user = new User();

      user.ID = (int)reader["userID"];
      // etc
}

现在假设我创建了一个继承自User的新对象,例如:
public class UserProfile : User
{
     public string Url {get;set;}
}

现在我需要创建一个加载用户资料的方法,所以目前我正在进行以下操作:
public UserProfile LoadUserProfile(SqlDataReader reader)
{
         UserProfile profile = new UserProfile();

         profile.ID = (int)reader["userID"];
         // etc. copying the same code from LoadUser(..)
         profile.Url = (string) reader["url"];
}

这句话的英译中是:“有没有更面向对象的方法,这样我就不必在LoadUserProfile()中从LoadUser()中复制我的代码?”“我希望我能这样做:”
UserProfile profile = new UserProfile();

profile = LoadUser(reader);

// and then init my profile related properties

这种事情能做到吗?

1
用户资料继承自用户?也许在这里使用组合比继承更有意义。 - Juliet
你也可以使用“IDataReader”代替“SqlDataReader”,这样如果将来更改数据库,就不必更改业务对象。 - Burnsys
11个回答

5
将LodUser方法移动到User Base类中并将其设置为虚拟方法。然后,在UserProfile方法中,您可以重写此方法。
public class User
{
   public virtual void Load(SqlDataReader reader)
   {
      this.Id = reader["Id"];
      //.. whatever else
   }
}

public class UserProfile : User
{
   public string ExtraProp {get;set;}
   public override void Load(SqlDataReader reader)
   {
      base.Load(reader);
      this.ExtraProp = reader["ExtraProp"];
   }
}

然后你可以像这样做:
UserProfile up = new UserProfile();
up.Load(myReader);

3

我不太理解为什么用户配置文件要继承自用户,从逻辑上讲,这对我来说没有太多意义。

对我来说有意义的是:

public class UserProfile
{
     User user;
     public string Url {get;set;}
}

那么你可以得到类似于如下的内容

public UserProfile LoadUserProfile(SqlDataReader reader)
{
         User user = LoadUser(reader);
         UserProfile profile = new UserProfile(user);

          //load profile stuff from reader...
         return profile;
}

4
对我来说,如果UserProfile是User类的成员,那将更有意义,因为我认为用户档案包含有关用户的信息,而不是反过来。 - Tom Neyland
@Tnay,是的...实际上我同意你的观点。我只是想使用他的方法和他所需的东西。 - Stan R.
用户和用户资料仅仅是例子,但我同意这个例子! - mrblah
1
一个改变问题的答案真的算是答案吗?用户/个人资料的例子可能会出现问题,但是将值读入子类的问题是一个合法的问题。 - dahlbyk

2

如果你不希望你的用户和用户资料对象知道 SQL(保持持久性无知 = 好),你可以使用操作现有对象的静态/扩展方法:

private static User LoadFromReader(this User user, SqlDataReader reader)
{
      user.ID = (int)reader["userID"];
      // etc

      return user;
}

public UserProfile LoadFromReader(this UserProfile profile, SqlDataReader reader)
{
     ((User)profile).LoadFromReader(reader);

     profile.Url = (string) reader["url"];
     // etc

     return profile;
}

那么调用代码可能如下所示:
UserProfile profile = new UserProfile().LoadFromReader(reader);

值得一提的是,您不一定需要使用静态/扩展方法,而是可以使用新的类/接口(易于模拟)来代替。重要的是将 User 的职责与 SQL 读取的职责分开。 - dahlbyk
我会使用具有工厂接口的工厂,而不是静态方法。 另外,“UserProfile profile = new UserProfile().LoadFromReader(reader)”感觉怪怪的。 我更喜欢:UserProfile profile = ProfileReader.LoadFromReader(reader); - tster
但是如果ProfileReader.LoadFromReader()和UserReader.LoadFromReader()都返回新实例,那么您不能使用后者来填充UserProfile,除非进行一些泛化的手势。工厂模式当然可以用于创建User / UserProfile,但它并没有解决子类的填充问题。 - dahlbyk

2
首先,为什么您的用户配置文件要继承自用户。UserProfile不是User,这是最糟糕的便利继承。

1
我会将它改为:
 class User 
 {
      public User(SqlDataReader reader)
      {
          Initialize(reader);
      }

      protected virtual void Initialize(SqlDataReader reader)
      {
           this.ID = (int)reader["userID"];
       // etc
      }
 }

 class UserProfile : User
 {
      public UserProfile(SqlDataReader reader) : base(reader) {}

      protected override void Initialize(SqlDataReader reader)
      {
           base.Initialize(reader); // Initialize "user" variables
           this.MyProperty = (int)reader["myProperty"];
      }
 }

这样,每个类都初始化自己的值(从构造函数中处理),而且你只需要在一个地方编写代码。


1
在我们到达UserProfile的Initialize之前,构造函数base(reader)不会调用base.Initialize(reader)吗? - TheVillageIdiot
@TheVillageIdiot:不可以,因为它是虚拟的并且被覆盖了。 - Powerlord

1
我建议在这里使用工厂类。这将使事物分开以满足单一职责原则。我还基于一个假设来回答您的问题,那就是User的构建属性都可以从UserProfile中访问。UserProfile的构造函数可以接受一个User并获取它的所有属性。
    public static class UserFactory
    {
        public static User LoadUser(SqlDataReader reader)
        {
            int id = (int)reader["userID"];
            return new User(id);
        }

        public static UserProfile LoadUserProfile(SqlDataReader reader)
        {
            User user = LoadUser(reader);
            // extra properties
            string url = (string)reader["url"];
            return new UserProfile(user, url);
        }
    }

0
创建一个接受读取器的构造函数:
public User(DbDataReader reader) {
   ID = reader.GetInt32(reader.GetOrdinal("userID"));
   // etc
}

使用方法:

User adam = new User(reader);

然后你可以在UserProfile中的构造函数中调用基类的构造函数。
public UserProfile(DbDataReader reader) : base(reader) {
   Url = reader.GetString(reader.GetOrdinal("url"));
}

0

我希望用户配置文件成为用户类的一个成员

public class User
{
//...all current members of the user class...

     UserProfile profile;
}

public class UserProfile
{

     public string Url {get;set;}

}

除此之外,我会按照Reed Copsey的回答所述进行操作。


0

听起来你想做类似以下的事情:

public class User { public Guid Id { get; set; } }
public class UserProfile : User { public string Url { get; set; } }

class MyReader
{
    private T LoadUser<T>(IDataReader reader)
        where T : User, new()
    {
        T user = new T();
        user.Id = reader.GetGuid(reader.GetOrdinal("userID"));
        return user;
    }

    public UserProfile LoadUserProfile(IDataReader reader)
    {
        UserProfile profile = LoadUser<UserProfile>(reader);
        profile.Url = (string)reader["url"];
        return profile;
    }
}

0

LoadUser设置为虚函数并公开。在UserProfile中进行重写。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接