简体   繁体   中英

Its is a good practice to cast from an interface to some concrete class when needed?

I'am developing a small system and i developed the classic generic repository. For now, i have the following architecture for my DAL.

public interface IRepositorio<T> where T : class
{
    T Get(long id);
    long Insert(T obj);
    bool Update(T obj);
    bool Delete(T obj);
}

public abstract class Repositorio<T> : IRepositorio<T> where T : class
{
    public IDbConnection Connection
    {
        get
        {
            return new SqlConnection(ConfigurationManager.ConnectionStrings["DBFila"].ConnectionString);
        }
    }

    public T Get(long id)
    {
        //...
    }

    public long Insert(T obj)
    {
        //...
    }

    public bool Update(T obj)
    {
        //...
    }

    public bool Delete(T obj)
    {
        //...
    }
}

My concrete repository looks like this:

public class FilaRepositorio : Repositorio<FilaRepositorio>
{
    public FilaRepositorio() 
    {
    }

    public void SomeCustomMethod()
    {
        // Some custom method
    }
}

I am also using Simple Injector to follow the IoC and DI patterns, for this reason, when i try to call "SomeCustomMethod()" i dont have access to it (obviously). Look:

public class Processador
{
    private IRepositorio<FilaModel> _repoFila;
    public Processador(IRepositorio<FilaModel> repoFila)
    {
        _repoFila = repoFila;
    }

    public void Processar()
    {
        _repoFila.SomeCustomMethod(); // <-- wrong

        ((FilaRepositorio)_repoFila).SomeCustomMethod();// <-- works
    }
}

Given this i have some questions:

  • Is a good or acceptable practice to make that cast (FilaRepositorio)?
  • If its not a good practice, how to write good code for this case?

There are a few options available. The main problem with making the cast is that it is an implementation concern.

What would happen if the injected object was not a FilaRepositorio ?

By making the cast you are tightly coupling the class to an implementation concern that is not guaranteed to be the inject dependency. Thus the constructor is not being entirely truthful about what it needs to perform its function.

This demonstrates the need to practice Explicit Dependencies Principle

The Explicit Dependencies Principle states:

Methods and classes should explicitly require (typically through method parameters or constructor parameters) any collaborating objects they need in order to function correctly.

One way to avoid it would be to make a derived interface that explicitly exposes the desired functionality of its dependents.

public interface IFilaRepositorio : IRepositorio<FilaModel> {
    void SomeCustomMethod();
}

public class FilaRepositorio : Repositorio<FilaModel>, IFilaRepositorio {
    public void SomeCustomMethod() {
        //...other code removed for brevity.
    }
}

and have the Processador depend on that more targeted abstraction.

Now there is no need for the cast at all and the class explicitly expresses what it needs.

public class Processador {
    private readonly IFilaRepositorio _repoFila;

    public Processador(IFilaRepositorio  repoFila) {
        _repoFila = repoFila;
    }

    public void Processar() {
        _repoFila.SomeCustomMethod(); // <-- works
    }
}

If you need to access a specific method from any part of your application, then that specific method must be part of your abstraction, or else there is no guarantee that you may use it when changing the concrete class.

I do not believe that your use of casting is a good idea at all, what is usually done in this case is to create a specific interface which defines any other method you could need to use:

public interface IFilaRepositorio : IRepositorio<Fila>
{
     void SomeCustomMethod();
}

And than use and declare that specific interface in any part of your code where you believe you need to use it:

public class Processador
{
    private IFilaRepositorio _repoFila;
    public Processador(IFilaRepositorio  repoFila)
    {
        _repoFila = repoFila;
    }

    public void Processar()
    {
        _repoFila.SomeCustomMethod();
    }
}

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