开发者

How to have my derived class only accept a specific derived type of another object

I have a couple different custom backup programs and I want to combine them into a single one. Doing so, I'm working on my OO design to help keep the driving program simple and easy to follow by abstracting my server and database information.

I'm using two different hosts (Discount ASP.net and Rackspace). Both of them go about their backups in a slightly different manner.

I've tried looking into a few different approaches, but here is the route that I think makes most sense.

I have my base class:

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

and then two derived classes:

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;
    }
}

The problem comes in with my related object Database. Since the backup processes are different for each host, I require different information for the databases. For example, for Discount ASP.net I need a version (2000, 2005, 2008) for the database so I know which of their web services to call. For Rackspace, I need the external server name, database username and passwords to create a connection string. Because of this, I tried creating a database object with the following hierarchy.

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); }
    }
}

What I want to do, is ensure that my DASPServer instance always gets in开发者_高级运维stances of DASPDatabases, and the same with Rackspace. Alternatively, I would like a smack in the head if I am going about this in the wrong direction.

Many thanks in advance


Make your DBServer class generic, then specify the actual Database type for the implementing classes.

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;
    }
}    


I would be inclined to abstract out the data access layer altogether - put it behind an interface (basically it's Dependency Inversion), then develop data access implementations that suit the backend you're dealing with. Th esituation you end up with is one where you simpoly drop a new concrete data access implementation into the bin directory (and appropriate config) and you're away - you don't have to recompile and redeploy the whole system.

Depending on the specifics of the dependancies you're dealing with you could even combine the two implementations into the same assembly. In this case it's more of a Strategy Pattern.

In both cases you can select the concrete data access implementation by config.


You could hide the base implementation of MakeBackupCall :

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

That way, if the method is called on a RackspaceServer variable, the compiler will enforce the type of the paramter to be BackspaceDatabase. But of course it won't work if it is called on a DBServer variable, since the base implementation would be called...

An acceptable compromise would be to do the actual implementation in a protected virtual method that takes a Database parameter :

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;
    }
}

That way, you can enforce the type at compile time when you explicitly use a BackspaceServer, and still use the derived implementation when you use a DbServer without knowing its actual type.

A similar approach is used in ADO.NET classes : for instance, SqlConnection.CreateCommand() returns a SqlCommand, even though the base DbConnection.CreateCommand() returns a DbCommand. The base method is hidden, and the actual implementation is protected abstract (CreateDbCommand)


Just check the type at runtime and throw an ArgumentException if the passed database is of the wrong type.


I'm not aware of any way to, at compile time, ensure that MakeBackupCall() uses the correct Database sub-class. At run-time, however, you can check that the passed parameter is of the correct sub-type.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜