Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C#

Why Use An Interface?

Rate me:
Please Sign up or sign in to vote.
4.69/5 (31 votes)
28 Dec 2017CPOL9 min read 28.6K   40   27
This post discusses why you should use an interface.

From time to time, I run into developers who ask the question, “what is the point of using an interface?” This is usually followed by a statement such as…

“I just new up a class whenever I need one, and don’t really see the point of using an interface.”

When I hear this, the first thing I do is to ask to see some of their unit tests.

Typically, this is met with a few moments of awkward silence, and then there is a declaration similar to… “Well, I don’t write unit tests. I don’t have time for them, and I really don’t see the point of them.” And my favorite… “Where’s the value add?”

Perhaps you have worked with developers like this.

I have sometimes observed that an associated trait of this approach to programming comes along with a statement or two on how “fast” they write their code.

Now, sometimes fast is good, but rarely at the expense of code that is tightly-coupled, untestable, or not object oriented. The reason for this is that according to Agile Development (2007), more time (as much as 80-90 percent of the life of the production code) is spent maintaining your code compared to the time spent initially writing it. So take the time to write it correctly using proven methods.

According to Osherove (2015) , tightly-coupled and untestable code will cost you and the team greatly in the long run in terms of bug fixes, changing existing features, or in terms of adding new features. Left to continue, some systems erode to the point where they just stop working and the only alternative is a complete re-write.

In any case, let’s suppose that our developer’s name is Bart. Everything goes along okay for a while, but then one day, inevitably, Bart is either out sick, or Bart decides to take some vacation.

Of course a bug is found, and the development manager asks you to take a look at Bart’s code, and please come up with a fix.

So you fire up your debugger and start walking though the code, and oh my, what do you find? — an assortment of monolithic classes and long methods, that in-turn, call other monolithic classes and long methods.

Since there are no interfaces in play, there are dependencies strewn about directly accessing things like databases, web services, the file system, EventLog, or any number of external dependencies. Logging is taking place, but each class creates an instance of the logging class as needed.

You understand that the logging code is a cross-cutting concern. However, you see that the same logging code repeats over and over again, and is scattered throughout many methods and classes. This adds additional noise to the code making it that much more unreadable.

Mr. Groves, in his book AOP in .NET (2015) discusses the advantages of Aspect Oriented Programming, (AOP — which relies on interfaces). Bart is not familiar with these techniques as he is not a fan of interfaces. However, an Aspect is precisely where the cross-cutting concerns like logging, security, authentication, and common things like defensive coding belong. (We will see how to create an Aspect to get rid of the repeating code in another post.)

In any case, perhaps you would like to write some tests for this code, but because there are no interfaces, there are no seams to pass in the required dependencies.

So you are stuck.

Most likely, you will run a console application, set break points, and try to find out where the bug lives. And when you think you’ve found a bug and want to make changes to the code, you’re left with a sinking feeling that perhaps when you check in your changes, you just might break something else.

Because Bart wrote no tests providing a safety net, at best, this approach is now a crap shoot.

Does this scenario sound familiar?

If so, you might be wondering, what does this have to do with interfaces?

The answer is: everything!

It is the use of an interface that allows us to begin to write decoupled code.

Each interface can serve as a seam where we can pass in a dependency of our own choice, real or faked, instead of relying on some hard coded reference to an external resource or other dependency such as the logging class dependency.

The power of the interface lies in the fact that an interface is nothing more than a required behavior. The interface is often called a contract, but I like to use the word behavior as the true focus and intent of an interface.

By stating the ‘behavioral intent’ through an interface, we “decouple” any concrete class’s implementation of the interface. Put another way, we are saying that any class that implements our interface must exhibit certain behaviors.

How the class implements the behavior, we no longer care. As long as the behavioral contract is implemented and followed, we are good to go!

Before we take a look at using an interface, let’s take a look at a class that does not use an interface, and hence provides us no seam.

This class uses an external dependency and its tight coupling prevents us from writing true unit tests. At best, we are forced to write an integration test that must call an external dependency.

Now, is this bad? I would argue three times: yes, yes, yes!

It is essential that we are able to test the behavior of our code without depending on external entities or services. These types of tests are called unit tests by definition. (If you follow Test Driven Development, you naturally will have testable and decoupled code, but this is the subject of another post.)

Integration tests that rely on external entities and other systems certainly have their place, but they do not replace the need for solid unit tests that allow us to “refactor with impunity.”

Let’s look at our Service class without an interface.

C#
public class Service
    {
        // HTTP implementation pseudocode
        public string GetUserFirstName(string logonId)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://myEndoint.com/");
            var result = request.GetResponse(logonId);
            return result.ToString();
        }
    }

We have no way to pass in our own dependency, because the Service class has taken on the hard coded responsibility of creating an HTTP request method. So even with this simple class and its one simple method, we are forever tied to an HTTP external dependency.

But what if the endpoint is down and we want to test our code, or we’d like to make and test some related business logic changes? We are pretty much stuck.

Let’s introduce a simple interface to see how our dependency situation changes.

The behavior that we want to enforce and test is in the GetUserFirstName method. So, let’s put that behind a simple interface and create our first seam.

C#
public interface IService
{
    string GetUserFirstName(string LogonId);
}

Now any class that implements our interface must provide an implementation of this method. Let’s do two things to leverage the interface’s behavior specification.

First, we will modify our existing Service class to require an IService instance as part of its constructor.

Secondly, we will have the Service class implement the IService interface.

In the first case, requiring an instance of the interface in the constructor is a typical dependency inversion technique.

We have moved the responsibility for creating the dependency from the Service class, and pushed it (or inverted it) back to the caller — hence the name Dependency Inversion.

The caller now must supply an instance of IService, and this is exactly what we want in our decoupled code!

In the second modification, implementing the IService interface, we want to guarantee that the GetUserFirstName method appears on the Service class.

Let’s take a look at the resulting Service class code to see what we have done.

C#
public class Service: IService
    {
        IService service;
        public Service(IService service)
        {
            this.service = service;
        }

        public string GetUserFirstName(string LogonId)
        {
            return service.GetUserFirstName(LogonId);
        }
    }

Notice that our interface is helping to shape our code!

By implementing IService, we have to provide the GetUserFirstName method.

We also leveraged the Service class’s constructor to require an instance of IService. Using the passed in instance of IService, we can now delegate the work of GetUserFirstName to whatever instance of IService we choose to pass in.

How do we know that we can successfully delegate our return call to service.GetUserFirstName(LogonId)?

We can be sure because we know any instance of IService must implement the GetUserFirstName method. Further, we required and set the local service variable from the Service class constructor to be of the IService type.

In our case, any class that implements the IService interface “IS” an IService and can be treated as such.

It is important to note that the interface behavior is at work for us by definition, both in the constructor and in the Service class method implementation.

By taking these simple steps, we have added polymorphic behavior to our code — and this is one of the pillars of object oriented programming.

Polymorphic means “many forms.” But in terms of our IService example, it simply means that the caller is not aware of the specific implementation details, and it does not need to be aware — it has merely to call the GetUserFirstName method and the work is handled by our IService instance.

Now the IService instance could be a web service class (we will have a separate class for each implementation), File System, SharePoint System, or it could be a test class implementation of IService; or any other implementation that may come along based on future requirements.

This constructor inversion technique is the basis for implementing the Strategy Pattern, where we “encapsulate what varies.” Many other design patterns in SOLID rely on Dependency Inversion to supply specific concrete instances of interfaces, based on the client’s needs.

The use of an IoC (Inversion of Control) container is often used to pre-wire and register these dependencies, relating each specific interface to a specific concrete class implementation. This is done in the root of the application at startup, known as the composition root. (IoC is the subject of another post.)

In any case, we have created our first seam, and it is this concept that enables decoupled and testable object oriented code.

Now, a unit test might easily be written that looks like this…

C#
[Test]
        [Category("Unit")]
        public void GetFirstName_UsingNamedTestService_ReturnsFirstNameFromTestService()
        {
            var factory = new Factory();
            IService instance = factory.FactoryMethod("testService");

            var result = instance.GetUserFirstName("101");

            Assert.AreEqual("FirstNameFromTestService", result);
        }

In my next post, I’ll demonstrate using structure map to create the factory method shown above. Without using a switch, the factory will return a specific instance of IService, solely based on the client passing in a simple string (the named instance.)

This named instance can be seen on line 6 in the unit test above, and is the stringtestService”.

This is the essence of the strategy pattern and is used throughout codebases by developers who adhere to the principles of SOLID programming.

All of this object oriented goodness is made possible thanks to the use of the Interface.

References

  • Agile Development and Software Maintenance Costs. (2007, March 01). Retrieved December 29, 2017, from http://tynerblain.com/blog/2007/02/28/agile-development-roi-2/
  • Groves, M. D. (2013). AOP in .NET: practical aspect-oriented programming. Shelter Island, NY: Manning Publications Co.
  • Osherove, R., Martin, R. C., & Feathers, M. (2015). Art of Unit Testing. Shelter Island, NY: Manning Publications Co.
This article was originally posted at http://JeffRonay.com/why-use-an-interface

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Jeff Ronay is a Sr. Software Engineer working for major banking institution.

Comments and Discussions

 
GeneralThe Service class is useless Pin
Paulo Zemek18-Jan-18 9:34
mvaPaulo Zemek18-Jan-18 9:34 
GeneralRe: The Service class is useless Pin
Jeff Ronay19-Jan-18 18:59
Jeff Ronay19-Jan-18 18:59 
GeneralRe: The Service class is useless Pin
Paulo Zemek20-Jan-18 11:12
mvaPaulo Zemek20-Jan-18 11:12 
GeneralRe: The Service class is useless Pin
Jeff Ronay20-Jan-18 13:28
Jeff Ronay20-Jan-18 13:28 
QuestionMy vote of 5 Pin
Sibeesh Passion5-Jan-18 0:55
professionalSibeesh Passion5-Jan-18 0:55 
AnswerRe: My vote of 5 Pin
Jeff Ronay5-Jan-18 7:25
Jeff Ronay5-Jan-18 7:25 
QuestionService taking an IService Pin
Joe Pizzi4-Jan-18 17:48
Joe Pizzi4-Jan-18 17:48 
AnswerRe: Service taking an IService Pin
Jeff Ronay4-Jan-18 18:58
Jeff Ronay4-Jan-18 18:58 
GeneralMy vote of 5 Pin
Degryse Kris2-Jan-18 1:06
Degryse Kris2-Jan-18 1:06 
GeneralRe: My vote of 5 Pin
Jeff Ronay2-Jan-18 15:43
Jeff Ronay2-Jan-18 15:43 
PraiseA note about interfaces and AOP Pin
mgroves29-Dec-17 15:29
mgroves29-Dec-17 15:29 
GeneralRe: A note about interfaces and AOP Pin
Jeff Ronay29-Dec-17 15:45
Jeff Ronay29-Dec-17 15:45 
QuestionThank you Pin
Member 1301043929-Dec-17 6:53
Member 1301043929-Dec-17 6:53 
AnswerRe: Thank you Pin
Jeff Ronay29-Dec-17 11:28
Jeff Ronay29-Dec-17 11:28 
QuestionGreat article Pin
Member 1359826728-Dec-17 11:10
Member 1359826728-Dec-17 11:10 
AnswerRe: Great article Pin
Jeff Ronay28-Dec-17 11:16
Jeff Ronay28-Dec-17 11:16 
QuestionGreat article Pin
Member 1180041228-Dec-17 8:20
Member 1180041228-Dec-17 8:20 
AnswerRe: Great article Pin
Jeff Ronay28-Dec-17 8:57
Jeff Ronay28-Dec-17 8:57 
QuestionIBookmarked Pin
_Vitor Garcia_28-Dec-17 4:15
_Vitor Garcia_28-Dec-17 4:15 
AnswerRe: IBookmarked Pin
Jeff Ronay28-Dec-17 8:58
Jeff Ronay28-Dec-17 8:58 
PraiseGreat article. Pin
GregoryPres28-Dec-17 0:30
GregoryPres28-Dec-17 0:30 
As a TDD practitioner, the use of interfaces is obvious to me, but we do forget that some might not see the value of it, not just in unit tests. Adhering to the SOLID principles - you're bound to use them.
GeneralRe: Great article. Pin
Jeff Ronay28-Dec-17 8:58
Jeff Ronay28-Dec-17 8:58 
QuestionQuestion re: IService implementation Pin
Member 1307923027-Dec-17 21:49
Member 1307923027-Dec-17 21:49 
AnswerRe: Question re: IService implementation Pin
Jeff Ronay28-Dec-17 5:03
Jeff Ronay28-Dec-17 5:03 
GeneralRe: Question re: IService implementation Pin
Member 130792305-Jan-18 2:08
Member 130792305-Jan-18 2:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.