-1

Good evening, I'm trying to implement SOLID principle with DI for the first time in a middle-size project. Most of the time I understand but now I have a trouble. Let me take a poor exemple but it shows the structure of the app. I have inherited classes with different constructors (code provided below). When I need an instance, I know the class I need. So, 2 questions about this code : 1/Do I have to use the interface in this case to achieve SOLID principe (or simply declare an instance of ClassA like ClassA exemple=new ClassA("text") in Program.cs) 2/Is it well done? (what is good/what is bad, what to do/what to avoid ?)

class Program
{
    static void Main(string[] args)
    {
        IFactory exemple = new Factory();
        //With Unity--> exemple=Resolve<IFactory>();
        exemple.GetTextClassA("test");
        exemple.GetTextClassB(1);

        Console.ReadLine();
    }
}

public interface IFactory
{
    ClassA GetTextClassA(string text);
    ClassB GetTextClassB(int text);
}

public class Factory : IFactory
{
    public ClassA GetTextClassA(string text)
    {
        return new ClassA(text);
    }
    public ClassB GetTextClassB(int text)
    {
        return new ClassB(text);
    }
}

public abstract class MainClass
{
    private string _text;

    public MainClass(){}

    protected abstract string GetText();
    protected virtual void Initialize()
    {
        _text = GetText();
        Console.WriteLine(_text);
    }
    public string TextToDisplay
    {
        get { return _text; }
    }
}

public class ClassA : MainClass
{
    string _textA;
    public ClassA(string textParam)
    {
        _textA = textParam;
        base.Initialize();
    }
    protected override string GetText()
    {
        return "Text is :"+_textA;
    }
}

public class ClassB : MainClass
{
    int _numParam;
    public ClassB(int numParam)
    {
        _numParam = numParam;
        base.Initialize();
    }
    protected override string GetText()
    {
        return "Text is :" + _numParam.ToString();
    }
}

Many thanks for your comments.

Gadsweb
  • 53
  • 10
  • What is your concrete problem? You are progamming a model, following a pattern/principle, when you want to reach a specific goal. What is your goal? When you don't go this way, to know this, we should not philosophize about problems, who are not existing, currently. – Teneko Oct 24 '17 at 23:17
  • I had problems maintaining/evoluting my previous project. I'm beginning a new one and I decided to be as close as possible from good practices. I read a lot a thing everywhere. In my case (specially with inherited classes), I want to decouple a maximum.and I don't know how to achieve that. – Gadsweb Oct 25 '17 at 00:09

1 Answers1

2

You don't seem to be injecting any dependencies.

The bare minimum for DI requires two things:

  1. Dependencies should be pushed, not pulled.
  2. The dependency graph should be composed in one place, at startup, in a place known as the composition root.

A very basic example

interface IServiceA
{
    void Foo();
}

interface IServiceB
{
    void Bar();
}

class ServiceA : IServiceA
{
    public void Foo() { do something ; }
}

class ServiceB : IServiceB
{
    public void Bar() { do something else; }
}

interface IProgram
{
    void Execute();
}

class Program : IProgram
{
    private readonly IServiceA _serviceA;
    private readonly IServiceB _serviceB;

    public Program(IServiceA serviceA, IServiceB serviceB)
    {
        _serviceA = serviceA;  //Injected dependency
        _serviceB = serviceB;  //Injected dependency
    }

    public void Execute()
    {
        _serviceA.Foo();
        _serviceB.Bar();
    }
}

void Main()
{
    //Composition root
    var container = new UnityContainer();
    container.RegisterType<IServiceA, ServiceA>();
    container.RegisterType<IServiceB, ServiceB>();
    container.RegisterType<IProgram, Program>();

    //The one and only one entry point for Program
    container.Resolve<IProgram>().Execute();
}

What is going on here?

Program is your main class that does everything. But it also does nothing. Under SRP, it offloads the interesting responsibilities to other classes (ServiceA and ServiceB). Only it doesn't know what the classes are-- just the interfaces it needs. And it doesn't know where they come from, or how they are created. They are just there, because they are pushed (injected) when Program is created.

The Unity container is set up with registrations for all the interfaces that Program needs. It will inject the dependencies into Program via constructor arguments (Unity does this for you automatically). The program can then call the services to get the job done, again, having no idea where they came from.

Why do we do it this way? A few reasons

  1. A test program can create other classes (stubs) that implement IServiceA and IServiceB. By injecting these stubbed dependencies, the test can focus solely on the logic in Program.

  2. By placing the specific interfaces as constructor arguments, it becomes immediately apparent exactly what Program depends on. If you get the wrong interface the program won't even compile. This is much better than waiting for runtime to find out a service you need isn't available (this is why we don't inject the Unity container itself-- you would't be able to tell what interfaces Program will try to pull without reading all of the code. Same with Service Locator pattern or simple static factories).

  3. By injecting dependencies using the container, you also allow for the dependencies to have dependencies (e.g. if ServiceA has a dependency on ServiceC). The Unity container will inject into the injected classes as well,k and inject into the injected injected classes, and so on.

  4. You manage lifetime (per-request, per-thread, or per-process) in the composition root, because that is where the responsibility lies. You don't want object lifetime logic scattered throughout the code base, as this makes it very hard to run the same object in, say, an application domain with threads per user versus a service or Windows application, which will have different lifetime rules.

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • DI is not the point for me here. I'm using Unity in other cases with a config file. I think (but not sure :-) ) that I understood this point. The question is more about using interfaces with an abstract class. As far as I know, we have to expose an interface for decoupling. But how to expose interfaces with inherited classes and different constructors. That's the main point for me in my exemple. – Gadsweb Oct 25 '17 at 00:16
  • I'm confused, your question contains "I'm trying to implement SOLID principle with DI". If you're trying to be SOLID then "When I need an instance, I know the class I need" is a problem-- your code should rely on interfaces and not know or care what the class is. If you don't have this mindset then it's not DI or SOLID. Are you asking for a workaround? – John Wu Oct 25 '17 at 00:27
  • Yes yes, for me everything is not also always clear. To be more precise, at this time, when I need ClassA (its methods) I do ClassA test=new ClassA() and after test.Method(). This is the standard way for me. But I want to use an interface and as it is an inherited class, I don't know how to implement. For me using an interface with inherited classes makes no sense... – Gadsweb Oct 25 '17 at 00:41