2
\$\begingroup\$

I have written a little bit of code which illustrates DI(dependency injection) and IoC(Inversion of control) container. The point of this exercise is to illustrate the two methods by which you can actieve inversion of control.

Please let me know if this code achieves the objective. (That is, if I have illustrated the two concepts clearly. Let me know if there is any improvement possible. (In any area like coding style, naming style, comments)

I have 4 classes for this:

  1. Dependencies: This class holds the interface and the two concrete implementations
  2. CoreClassDI: This class illustrates dependency injection
  3. ControlInversionAid: This holds a simple IoC container following instructions from here. Hence I will not be sharing this code for review.
  4. Program: This is the console application that illustrates the concepts
  5. WritePredictions: This invokes the method on the dependency

Now, for the code:

Dependencies

namespace Dependencies
{
    public interface IPredictingFuture
    {
        string WhatWillBeGoodNextYear();
    }

    public class EartAndSkyPrediction : IPredictingFuture 
    {
        public EartAndSkyPrediction()
        {
            Console.ForegroundColor = ConsoleColor.DarkGreen;
        }

        public string WhatWillBeGoodNextYear()
        {
            return ("Everything will be good next year, especially if you are good!");
        }
    }

    public class BadConnections : IPredictingFuture 
    {
        public BadConnections()
        {
            Console.ForegroundColor = ConsoleColor.Red;
        }

        public string WhatWillBeGoodNextYear()
        {
            return ("Take care!! It is a terrible year :-(");
        }
    }
}

CoreClassDI

namespace CoreClassDI
{
    public class TheOracle
    {
        //Property Injection. This is injecting the correct concrete implementation
        //through a property. One can also use a constructor or an 
        //interface implementation to inject dependencies.
        public IPredictingFuture FuturePredictions { get; private set; }

        //This is a predict function in which you "inject" the concrete class
        //in the property. The property injection is achieved through the constructor.
        //We can also alternatively inject dependencies through the WriteOutPredictions()
        //function.
        public TheOracle(IPredictingFuture predictionType)
        {
            FuturePredictions = predictionType;
        }
    }
}

WritePredictions

namespace ClientCode
{
    //This is the class which actually writes out the predictions based on the 
    //actual class loaded either through Injection or the IoC container
    public class WritePredictions
    {
        public static void WritePredictionsOnScreen(IPredictingFuture predictIt)
        {
            Console.WriteLine("How will my year be?");
            Console.WriteLine(predictIt.WhatWillBeGoodNextYear());

            Console.WriteLine("**********");
        }
    }
}

Program

namespace ClientCode
{
    //This is the main program which controls and illustrates the different types of DI
    //You need to start debug in one of these programs to understand the concepts
    //of DI and IoC
    class Program
    {
        static void Main(string[] args)
        {
            string choice;
            string input;
            Console.WriteLine("Choose the correct option");
            Console.WriteLine("1 for dependency injection");
            Console.WriteLine("2 for IoC container");
            input = Console.ReadKey().KeyChar.ToString();
            switch (input)
            {
                case "1":
                    Console.WriteLine("Welcome! Do you want a good prediction? Or a Bad prediction?");

                    Console.WriteLine("Hit G for good predictions and B for bad predictions");

                    choice = Console.ReadKey().KeyChar.ToString();
                    IPredictingFuture predictionObj = null;

                    //The dependency is loaded based on run time choice
                    switch (choice)
                    {
                        case "G":
                        case "g":
                            //Setting good predictions
                            predictionObj = new EartAndSkyPrediction();

                            break;
                        case "B":
                        case "b":
                            //Setting bad predictions on the same object
                            predictionObj = new BadConnections();

                            break;
                        default:
                            Console.WriteLine("Good going! Who cares anyway?");
                            break;
                    }
                    //Injecting the prediction object and calling the client code with it
                    TheOracle oracleObj = new TheOracle(predictionObj);
                    WritePredictions.WritePredictionsOnScreen(oracleObj.FuturePredictions);
                    Console.ReadKey(true);
                    break;

                case "2":
                    Console.WriteLine("Welcome! Do you want a good prediction? Or a Bad prediction?");

                    Console.WriteLine("Hit G for good predictions and B for bad predictions");

                    choice = Console.ReadKey().KeyChar.ToString();
                    switch (choice)
                    {
                        case "G":
                        case "g":
                            //Registering the good predictions
                            IoC.Register<IPredictingFuture, EartAndSkyPrediction>();
                            break;
                        case "B":
                        case "b":
                            //Loading the bad predictions
                            IoC.Register<IPredictingFuture, BadConnections>();
                            break;
                        default:
                            Console.WriteLine("Good going! Who cares anyway?");
                            Console.ReadKey(true);
                            break;
                    }
                    //Resolving to the registered predictions
                    WritePredictions.WritePredictionsOnScreen(IoC.Resolve<IPredictingFuture>());
                    Console.ReadKey(true);
                    break;
                default:
                    Console.WriteLine("Invalid choice");
                    Console.ReadKey(true);
                    break;
            }
        }
    }
}
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

Your example does show IoC tecnhique by passing interface to WritePredictionsOnScreen rather than actual implementation, but doesn't show the power of DI containers like auto-wiring and object lifetime management.

In your example IoC container looks like just a replacement of a local variable that holds the instance IPredictingFuture.

Example of DI autowiring using Autofac (use Install-Package Autofac in NuGet console to install it). I borrowed paritosh's definition of Oracle, it is more appropriate and demonstrates the IoC better than original code. I register TheOracle before registering classes it depends on to show that the order is not significant here.

Note that FuturePredictions is initialized through constructor while Writer is injected via property (just for demonstration, it would be better to use common approach):

public interface IPredictingFuture
{
    string WhatWillBeGoodNextYear();
}

public interface IWriter
{
    void WriteMsg(string message);
}

public class EartAndSkyPrediction : IPredictingFuture
{
    public EartAndSkyPrediction()
    {
        Console.ForegroundColor = ConsoleColor.DarkGreen;
    }

    public string WhatWillBeGoodNextYear()
    {
        return ("Everything will be good next year, especially if you are good!");
    }
}

public class BadConnections : IPredictingFuture
{
    public BadConnections()
    {
        Console.ForegroundColor = ConsoleColor.Red;
    }

    public string WhatWillBeGoodNextYear()
    {
        return ("Take care!! It is a terrible year :-(");
    }
}

public class TheOracle
{
    public IWriter Writer { get; set; }

    public IPredictingFuture FuturePredictions { get; private set; }

    //This is a predict function in which you "inject" the concrete class
    //in the property. The property injection is achieved through the constructor.
    //We can also alternatively inject dependencies through the WriteOutPredictions()
    //function.
    public TheOracle(IPredictingFuture predictionType)
    {
        FuturePredictions = predictionType;
    }

    public void PredictAndWriteToLogger()
    {
        Writer.WriteMsg(FuturePredictions.WhatWillBeGoodNextYear());
    }
}

public class WritePredictions : IWriter
{
    public void WriteMsg(string message)
    {
        Console.WriteLine("How will my year be?");
        Console.WriteLine(message);

        Console.WriteLine("**********");
    }
}

internal class Program
{
    private static void Main()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<TheOracle>().PropertiesAutowired();
        builder.RegisterType<WritePredictions>().AsImplementedInterfaces();

        Console.WriteLine("Welcome! Do you want a good prediction? Or a Bad prediction?");
        Console.WriteLine("Hit G for good predictions and B for bad predictions");

        var choice = Console.ReadKey().KeyChar.ToString();
        switch (choice)
        {
            case "G":
            case "g":
                //Registering the good predictions
                builder.RegisterType<EartAndSkyPrediction>().AsImplementedInterfaces();
                break;
            case "B":
            case "b":
                //Loading the bad predictions
                builder.RegisterType<BadConnections>().AsImplementedInterfaces();
                break;
            default:
                Console.WriteLine("Good going! Who cares anyway?");
                Console.ReadKey(true);
                break;
        }

        var container = builder.Build();
        var theOracle = container.Resolve<TheOracle>();
        theOracle.PredictAndWriteToLogger();
    }
}
\$\endgroup\$
7
  • \$\begingroup\$ Can you give me a small example for illustrating the power of DI? Also, how else do you think an IoC container needs to be used? Can you give me an example? \$\endgroup\$ Commented Dec 20, 2012 at 11:48
  • \$\begingroup\$ @TheSilverBullet updated my answer \$\endgroup\$
    – almaz
    Commented Dec 20, 2012 at 12:15
  • \$\begingroup\$ I wanted to use my own IoC container so that I would understand it better. I would ofcourse shift to Autofac later. \$\endgroup\$ Commented Dec 20, 2012 at 13:50
  • \$\begingroup\$ Looks like you have included DI and use of IoC container in lesser piece of code than I originally wrote. This is good! Thanks! But one thing I did not understand is how you injected the Writer. Can you point me to the line specifically... (Sorry!) \$\endgroup\$ Commented Dec 20, 2012 at 13:52
  • \$\begingroup\$ builder.RegisterType<WritePredictions>().AsImplementedInterfaces(); - I got the line. So once this line executes, would the IWriter property be set to WritePredictions? \$\endgroup\$ Commented Dec 20, 2012 at 13:59
2
\$\begingroup\$

Possible Improvements of above code:

WritePredictions class should not be responsible for calling the WhatWillBeGoodNextYear method of Prediction, this is the job of TheOracle Class.I have created IWriter interface for switching out the Output console. so WritePredictions class is not needed.

    public class TheOracle
    {
        public IWriter Writer { get; set; }

        public IPredictingFuture FuturePredictions { get; private set; }

        //This is a predict function in which you "inject" the concrete class
        //in the property. The property injection is achieved through the constructor.
        //We can also alternatively inject dependencies through the WriteOutPredictions()
        //function.
        public TheOracle(IPredictingFuture predictionType)
        {
            FuturePredictions = predictionType;
        }

        public void PredictAndWriteToLogger()
        {
            Writer.WriteMsg(FuturePredictions.WhatWillBeGoodNextYear());
        }
    }

    public interface IWriter
    {
      void WriteMsg(string message);
    }

rest of code looks okay to me.You can also return string from your Predict method and let client to decide whether to write.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks for this tip. It definitely makes the code more elegant! \$\endgroup\$ Commented Dec 20, 2012 at 11:46
  • \$\begingroup\$ Paritosh, I did take your idea of adding IWriter interface. I moved the message writing functionality to the concrete implementation of IPrediction interface. TheOracle class is used to illustrate dependency injection. Hence I did not add this there. I will be renaming that class to avoid confustion. \$\endgroup\$ Commented Dec 21, 2012 at 11:08
  • \$\begingroup\$ No problem,my intent is that you understood aspects of DI and IOC. No code is gud or bad , untill it serve it purpose. \$\endgroup\$
    – Paritosh
    Commented Dec 21, 2012 at 11:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.