简体   繁体   中英

How to point to generic abstract class?

I haven't got problems with code since my last problem with interface covariance. Today I took another approach to the OLGAtherer architecture and I found huge (for me) obstacle. I hope that its huge only for me and I will find answer here :) OK, please check this out:

I have abstract generic class:

abstract class Repository<T> where T : Entity

this class contain signatures of four CRUD methods (add, insert, etc.) and two protected fields. Any repository that I will create must inherit from this superclass. I have repositories like, for example:

class BookRepository : Repository<Book>

Now I have class DB that is responsible for operations on DB file. And there is my problem. Look at this method placed in DB class:

    void Add(List<Entity> enitites, Type entityType)
    {
       Repository<entityType> repo = EntitiesFactory(entityType);
       repo.Add(entities);
    }

I know that above method won't work, but this visualize my problem (hopefully) - I would like to dynamically create repository of the specified type, but I have no idea how to do this. This has been always my main OOP problem - I could code inheritance (or implementation of interfaces) but I could not use these inherited classes. Please clarify this to me. Thank you in advance.

Paweł


I used hints that you provided and this is what I have now:

public void Add<T>(List<T> entities, string repoName) where T : Entity
{
   Repository<T> repo = RepoFactory<T>(repoName, typeof(T));
   repo.Add(entities);
}

private Repository<T> RepoFactory<T>(string repoName, Type t) where T : Entity
{
   if (t == typeof(Book))
   {
   Repository<T> repo = (Repository<T>)((object)(new BookRepository(this.ConnectionString, repoName)));
   }

   return null;

}

RepoFactory is now in DB class (which is, by the way, some sort of Bridge here) - it should be moved elsewhere probably, but it's not a main problem here.

First, I don't know whether RepoFactory body is implemented in the proper way - I guess not.

Second - I assume that I will call DB methods from other class (let's say - WindowsForm, for simplicity). I will call Add with parameter T dependent on user's choice. This is situation that I have problem and fix it temporarily, because now DB is OK, but if I would have to implement WindowsForm code, I will encounter it another time (I think) - in the future I will try to figure out answer to the same question, but on the higher level (above DB) - I hope you understand me... I basically mean, that I will not know how to dynamically call Add method, because T will be dependent on user's choice (OLGAtherer has to be Repository Manager that would support Books and other kind of collections). And if user will try to work with his book collection, I will have to call Add() method. If with Comics collection, Add(). I don't want to write class above for each kind of collection, but rather use one call of Add to do this.

If you understand what I mean, you're really good :) My English sucks, but I'm working on it. Thank you for previous answers and thanks in advance for further ones. Paweł

I'm not quite sure what you want. Will this do?

void Add<T>(List<T> entities) where T : Entity
{
   Repository<T> repo = EntitiesFactory(typeof(T));
   repo.Add(entities);
}

Also, I suggest that you be as flexible as possible with input parameters, eg use IEnumerable<T> instead of List<T> for this method and also in the Repository<T>.Add() method. You might want to pass in a lazily-evaluated query, or chain multiple LINQ methods together.

You can make the Add method generic.

void Add<T>(List<T> enitites)
{
   Repository<T> repo = EntitiesFactory(typeof(T));
   repo.Add(entities);
}

You can make it type-safe by modifying your object hiearchy with one more layer of indirection as follows:

public abstract class EntityCollection<T> : List<T>
    where T : Entity
{
    public abstract Repository<T> GetRepository();
}

public class BookCollection : EntityCollection<Book>
{
    public override Repository<Book> GetRepository()
    {
        return BookRepository();
    }
}

So now you can modify your DB class as follows:

public void Add<T>(EntityCollection<T> entities)
{
    Repository<T> repo = entities.GetRepository();
    repo.Add(entities);
}

So now you can pass an instance of BookCollection into your Add method and it will work in a 100% type-safe, transparent manner.

Just to add to everyone else's answers, you may want to take a look at the Inversion of Control (IoC) design pattern as well. It may simplify your life in future work.

With dependency injection, which is what IoC gives you, you can significantly simplify your use of the EntitiesFactory, for example.

I'm using StructureMap at the moment to implement IoC and it's working well for me.

Here's Martin Fowler's page on IoC, and Wikipedia's , to get you started.

Whatever you are returning in EntitiesFactory() is what you need to use. So the problem is really with code which you have not shown. Show the code for EntitiesFactory() and we get a better idea on what you are trying to accomplish.

NOTE: You understand that you cannot overload return types (unfortunately).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM