Ted
Ted

Reputation: 23

Using Interface the right way?

I read some articles about interfaces, but something it's not clear enough for me yet. Please help out finding the right way working with interfaces. My question is within the comments in codesample:

using System;
namespace IfTest
{
    public interface ICalculator
    {
        void Sum(int a, int b);
    }

    public class MyCalc : ICalculator
    {
        public void Sum(int a, int b)
        {
            Console.WriteLine(a + b);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //What's the difference between
            ICalculator mycalc;
            mycalc = new MyCalc();
            mycalc.Sum(5, 5);

            //and this. When should we use this way?
            MyCalc mc = new MyCalc();
            mc.Sum(5, 5);
        }
    }
}

Upvotes: 1

Views: 2213

Answers (4)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112259

Interfaces come into full effect when Dependency injection is used. Assume that you have a method that does calculations by using a calculator. Instead of instantiating the calculator in this method, pass it a calculator as argument:

 public void PerformSomeCalculations(ICalculator calculator)
 {
     calculator.Sum(5, 5);
     ...
}

This allows you to change the calculator by another implementation at any time. The method PerformSomeCalculations does not know anything about a specific implementation. For testing purposes you could even pass it a dummy calculator that tracks the methods that have been called, in order to see if the method PerformSomeCalculations does what it is expected to do.

You could even provide a calculator that does Modular arithmetic for instance, and compare the behavior of two very different calculators.

Programming versus interfaces makes your code more flexible than programming versus specific types. Programming versus interfaces makes Unit testing easier.

Upvotes: 1

Bojan Komazec
Bojan Komazec

Reputation: 9526

You will usually have a situation where one class uses services of some other class. "uses services" means calls its public methods. For example, CalculatorUser uses MyCalc - CalculatorUser instance will somewhere call MyCalc.Sum().

Imagine now that you give to your customer your application which contains classes CalculatorUser and MyCalc. You can write something like this:

public class CalculatorUser
{
    private MyCalc _myCalc;

    public CalculatorUser(MyCalc myCalc)
    {
        _myCalc = myCalc;
    }

    public void PerformAddition(int a, int b)
    {
        _myCalc.Sum(a, b);
    }
}

class Program
{
    static void Main(string[] args)
    {
        MyCalc calculator = MyCalc();
        CalculatorUser calcUser = new CalculatorUser(calculator);
        calcUser.PerformAddition(1, 2);

        Console.ReadKey();
    }
}

Everything looks good but then after some time that customer comes back to you with a new requirement: "I want CalculatorUser to have more choice: I want it to be able to choose between old, simple calculator MyCalc and a new, fancy calculator which displays operands, operation and result! Also, this choice has to be made in the runtime."

You realize that now you have to create MyFancyCalc and also to change CalculatorUser in order to support this new requirement. You might want to add another member of type MyFancyCalc to CalculatorUser and then another method PerformAdditionWithFancyCalc() which would use MyFancyCalc. But what if your customer comes with requirement for 10 another types of calculators - would you be adding new members and methods for each of them? If you keep user and service provider tightly coupled, every change in requirements will lead to constant changes in the user and solution to this is having user not to know about particular service provider but only about the services it provides: what are names of those services, what are types of their input values, what are their output types? This is actually what makes the public interface of the service provider. CalculatorUser does not need to know for particular implementation of calculator - MyCalc or MyFancyCalc, all it has to know is that any calculator it uses has a method Sum which accepts two int values and returns void. By this way you are decoupling user from particular calculator and makes it being able to use any calculator which implements Sum in the way described in the interface. If you create MyExtraFancyCalc class you will not need to change CalculatorUser.

So, to satisfy new requirement (making calculator choice in the runtime), you could write something like this:

public interface ICalculator
{
    void Sum(int a, int b);
}

public class MyCalc : ICalculator
{
    public void Sum(int a, int b)
    {
        Console.WriteLine(a + b);
    }
}

public class MyFancyCalc : ICalculator
{
    public void Sum(int a, int b)
    {
        Console.WriteLine("{0} + {1} = {2}", a, b, a + b);
    }
}

public class CalculatorUser
{
    private ICalculator _calculator;

    public CalculatorUser(ICalculator calculator)
    {
        _calculator = calculator;
    }

    public void PerformAddition(int a, int b)
    {
        _calculator.Sum(a, b);
    }
}

class Program
{
    static void Main(string[] args)
    {
        bool useFancyCalculator = GetUseFancyCalculator();

        ICalculator calculator = CreateCalculator(useFancyCalculator);
        CalculatorUser calcUser = new CalculatorUser(calculator);
        calcUser.PerformAddition(1, 2);

        Console.ReadKey();
    }

    static bool GetUseFancyCalculator()
    {
        Console.WriteLine("Would you like to use fancy calculator? (y/n)");
        string choice = Console.ReadLine();
        return (choice == "y");
    }

    static ICalculator CreateCalculator(bool createFancyCalculator)
    {
        ICalculator calculator = null;

        if (createFancyCalculator)
            calculator = new MyFancyCalc();
        else
            calculator = new MyCalc();

        return calculator;
    }
}

User is asked "Would you like to use fancy calculator? (y/n)" and if types "n", old calculator is used and the output is simply "3" but if answer is "y", fancy calculator is used and output is "1 + 2 = 3"

This example shows the power of interfaces (and basically one simple case of pattern called Dependency Injection).

In the real life you would more often have situation where your customers have an app with consumer (CalculatorUser) which never or very rarely changes and plugins (DLLs) which contain various implementations of the service provider. Main app detects in the runtime which plugins are available and picks one, depending on the user choice (or some other criteria) generated in the runtime.

Upvotes: 1

rae1
rae1

Reputation: 6134

You use interfaces when you want your program to depend upon abstract contracts, rather than hard implementations. By using the ICalculator interface to define a variable (or field), you allow for future implementations of the program to provide other implementations of that contract,

ICalculator mycalc;
mycalc = new MyCalc(); // or mycalc = new OtherCalc(); or mycalc = new FinancialCalc();

In the future, when a more efficient implementation comes along, you can quickly substitute mycalc with a different class that implements the interface, and your program will continue to behave as expected. This is specially important if you do not want your program to fail due to unknown side effects or implementation details provided by a hard implementation like MyCalc.

Upvotes: 0

Darin Dimitrov
Darin Dimitrov

Reputation: 1038710

With ICalculator mycalc you are declaring a variable of type ICalculator and at compile-time you will only be able to invoke methods which are part of this interface. You will not be able to invoke a method which is not part of the interface but only of the implementing class.

With MyCalc mc you are declaring a variable of type MyCalc and at compile-time you will only be able to invoke all methods on this class and on the interface it inherits.

At runtime there will be no difference at all between the two.

When programming against interfaces it is recommended to work against the most abstract possible type in your object hierarchy. So in this case that would be the ICalculator interface.

This allows for better separation between the calling code and the actual implementation. If the calling code is programmed against an interface it is no longer tied to the specific MyCalc implementation which could be swapped with some other implementation.

Upvotes: 5

Related Questions