如何让我的派生类只接受另一个对象的特定派生类型

时间:2010-07-29 21:48:30

标签: c# .net architecture inheritance oop

我有几个不同的自定义备份程序,我想将它们合并为一个。 这样做,我正在研究我的OO设计,通过抽象我的服务器和数据库信息来帮助保持驱动程序简单易用。

我正在使用两个不同的主机(Discount ASP.net和Rackspace)。他们都以略微不同的方式进行备份。

我尝试过几种不同的方法,但这是我认为最有意义的路线。

我有我的基类:

public abstract class DBServer
{
    public List<Database> Databases { get; set; }
    public abstract bool MakeBackupCall(Database d);
}

然后是两个派生类:

public class DASPServer : DBServer
{
    public string APIKey { get; set; }

    public override bool MakeBackupCall(Database d)
    {
         // do some stuff
         return true;
    }
}

public class RackspaceServer : DBServer
{
    public override bool MakeBackupCall(Database d)
    {
        // do some different stuff
        return true;
    }
}

问题来自我的相关对象Database。 由于每个主机的备份过程不同,因此我需要不同的数据库信息。例如,对于Discount ASP.net,我需要一个版本(2000,2005,2008)用于数据库,因此我知道要调用哪些Web服务。对于Rackspace,我需要外部服务器名称,数据库用户名和密码来创建连接字符串。因此,我尝试使用以下层次结构创建数据库对象。

public abstract class Database
{
    public string Name { get; set; }     
}

public class DASPDatabase : Database
{
    public string Version { get; set; }
}

public class RackspaceDatabase : Database
{
    public string ServerName { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }

    protected string ConnectionString
    {
        get { return string.Format("Data Source={0};Network Library=;Connection Timeout=30;Packet Size=4096;Integrated Security=no;Encrypt=no;Initial Catalog={1};User ID={2};Password={3}", ServerName, Name, UserName, Password); }
    }
}

我想要做的是确保我的DASPServer实例始终获取DASPDatabases的实例,并且与Rackspace相同。或者,如果我朝错误的方向前进,我想要头脑清醒。

非常感谢提前

5 个答案:

答案 0 :(得分:3)

使您的DBServer类具有通用性,然后指定实现类的实际数据库类型。

public abstract class DBServer<TDatabase> where TDatabase : Database
{
    public List<TDatabase> Databases { get; set; }
    public abstract bool MakeBackupCall( TDatabase d );
}

public class DASPServer : DBServer<DASPDatabase>
{
    public string APIKey { get; set; }

    public override bool MakeBackupCall( DASPDatabase d )
    {
        // do some stuff
        return true;
    }
}

public class RackspaceServer : DBServer<RackspaceDatabase>
{
    public override bool MakeBackupCall( RackspaceDatabase d )
    {
        // do some different stuff
        return true;
    }
}    

答案 1 :(得分:1)

您可以隐藏MakeBackupCall的基本实现:

public new bool MakeBackupCall(RackspaceDatabase d)
{
    // Do something
}

这样,如果在RackspaceServer变量上调用该方法,编译器将强制该参数的类型为BackspaceDatabase。但是当然如果在DBServer变量上调用它将无法工作,因为将调用基本实现...

可接受的折衷方案是在采用Database参数的受保护虚拟方法中进行实际实现:

public abstract class DBServer
{
    public List<Database> Databases { get; set; }

    // Non-virtual; the actual implementation is not done in that method anyway
    public bool MakeBackupCall(Database d)
    {
        return MakeBackupCallCore(d);
    }

    // Actual implementation goes there
    protected abstract MakeBackupCallCore(Database d);
}

public class RackspaceServer : DBServer
{
    // Hide the base method
    public new bool MakeBackupCall(BackspaceDatabase d)
    {
        return MakeBackupCallCore(d);
    }

    // Do the actual implementation here, and ensure it is really a BackspaceDatabase
    protected virtual bool MakeBackupCallCore(Database d)
    {
        BackspaceDatabase bd = d as BackspaceDatabase;
        if (bd == null)
            throw new ArgumentException("d must be a BackspaceDatabase");

        // do some different stuff with bd
        return true;
    }
}

这样,您可以在显式使用BackspaceServer时在编译时强制执行该类型,并在使用DbServer时仍然使用派生实现,而不知道其实际类型。

在ADO.NET类中使用了类似的方法:例如,SqlConnection.CreateCommand()返回SqlCommand,即使基类DbConnection.CreateCommand()返回DbCommand。基本方法是隐藏的,实际的实现是受保护的抽象(CreateDbCommand

答案 2 :(得分:1)

我倾向于完全抽象出数据访问层 - 将它放在一个接口(基本上是Dependency Inversion)之后,然后开发适合你正在处理的后端的数据访问实现。您最终得到的结果是,您可以简单地将新的具体数据访问实现放入bin目录(和适当的配置),然后您就可以了 - 您不必重新编译和重新部署整个系统。

根据您正在处理的依赖项的具体情况,您甚至可以将两个实现组合到同一个程序集中。在这种情况下,它更像是Strategy Pattern

在这两种情况下,您都可以通过配置选择具体的数据访问实现。

答案 3 :(得分:0)

只需在运行时检查类型,如果传递的数据库类型错误,则抛出ArgumentException

答案 4 :(得分:0)

在编译时,我不知道有任何方法确保MakeBackupCall()使用正确的Database子类。但是,在运行时,您可以检查传递的参数is是否为正确的子类型。