MistyK
MistyK

Reputation: 6232

Dependency injection and additional functionality

this is my general problem so I will boil it down to something very small. Let's have an interface for generic service.

FIRST PART

public interface IGenericService<T>
{
    void Create(T add);
    void Read();
    void Update(T obj);
    void Delete(T obj);
}

And it's implementations:

 public class DogService : IGenericService<Dog>
 {
     void Create(Dog add){}
     void Read(){}
     void Update(Dog obj){}
     void Delete(Dog obj){}
 }

And second repo which has some unique, additional functionality like Miau()

 public class CatService: IGenericService<Cat>
 {
     void Create(Cat add){}
     void Read(){}
     void Update(Cat obj){}
     void Delete(Cat obj){}
     void Miau(){}
 }

Now when using some IOC's I would go for:

Bind<IGenericService<Dog>,DogService>
Bind<IGenericService<Cat>,CatService>

Now somewhere in Viewmodel:

public class CatsViewModel
{
    public CatsViewModel(IGenericService<Cat> catService)
    {
        //how to have Miau functionality here?
    }
}

1.How can I have Miau functionality here? Should I make second interface for DogService like IDogService and use it this way in here? What is the purpose of generic repo then ?

SECOND PART Let's have GenericViewModel like this:

public abstract class GenericViewModel<T>
{
    T Collection { get; }
    public GenericViewModel(IGenericService<T> service)
    {
        Collection = service.Read();
    }
}

And this is fine. But what if I want to use it with CatService

public class CatViewModel : GenericViewModel<Cat>
{
    public CatViewModel(IGenericService<T> service) : base(service)
    {
        // make miau here?
    } 
}
  1. Should I create ICatService interface and inject the instance of the same service with two different interfaces? I know I can cast it from IGenericService since I know what kind of service this is but is it a good approach?

    These questions are about good habits not about solution which just works :)

Upvotes: 3

Views: 133

Answers (1)

Mark Seemann
Mark Seemann

Reputation: 233457

A few options come to mind:

Non-generic interfaces

What is the purpose of generic repo then ?

Indeed, what's the purpose of it? How hard would it be to type out an interface for each purpose?

public interface IDogService
{
    void Create(Dog add);
    void Read();
    void Update(Dog obj);
    void Delete(Dog obj);
}

public interface ICatService
{
    void Create(Cat add);
    void Read();
    void Update(Cat obj);
    void Delete(Cat obj);
    void Miau();
}

Now each consumer can request the dependency that they need. Clients of ICatService can consume Miau.

Pass the extra behaviour as an extra dependency

Another option is to keep the generic IGenericService<T>, but add another service for the Miau functionality:

public interface IMiauService
{
    void Miau();
}

Then let the clients that need IMiauService request it via their constructors:

public class CatsViewModel
{
    private readonly IGenericService<Cat> catService;
    private readonly IMiauService miauService;

    public CatsViewModel(IGenericService<Cat> catService, IMiauService miauService)
    {
        this.catService = catService;
        this.miauService = miauService;
    }

    // Members can use both services as needed...
}

This solution hints at a third, and better, option:

Follow the Dependency Inversion Principle

According to the Dependency Inversion Principle, the clients should define the API they need, and then it's up to concrete classes to implement that API.

Thus, if a class needs read and write functionality, it should advertise that need by declaring corresponding interfaces:

public interface IReader<T>
{
    T Reader();
}

public interface IWriter<T>
{
    void Write(T item);
}

public class GenericViewModel<T>
{
    private readonly IReader<T> reader;
    private readonly IWriter<T> writer;

    public GenericViewModel(IReader<T> reader, IWriter<T> writer)
    {
        this.reader = reader;
        this.writer = writer;
    }

    // Members can use the reader and writer services...
}

An interface like the OP IGenericService<T> smells like something that's been defined before the need was identified, and now you're trying to fit a square peg into a round hole.

Upvotes: 1

Related Questions