HowTo: Interfaces/Schnittstellen einsetzen – Gute Gründe für den Einsatz von Schnittstellen

In einem HowTo ging es darum, was Interface eigentlich sind und wo man sowas einsetzen könnte. In vielen OOP Büchern wird immer wieder hervorgehoben, warum es so wichtig ist, “auf eine Schnittstelle zu programmieren” und nicht “auf eine konkrete Klasse“. Bis vor kurzem war es mir selber noch nicht ganz klar, wozu dieser Aufriss mit den ganzen Schnittstellen, bis ich mit “Test Driven Development” angefangen habe.

Häufige Argumentation für Interfaces:

Wenn man sich etwas mit den Interfaces beschäftigt, dann kommt man immer wieder zum Datenbankbeispiel. “Stellen wir uns doch mal vor, die Datenquelle wechselt!”

Wie könnte ein Interface an dieser Stelle helfen?

An dieser Stelle können Interface helfen, indem sie wie eine Art “Vertrag” wirken:

interface IDataProvider
{
	List<string> GetData();
}

class OracleDataProvider : IDataProvider
{
	List<string> GetData()
	{
	// Oracle Zeugs
	}
}

class SqlDataProvider : IDataProvider
{
	List<string> GetData()
	{
	//MS Sql Zeugs
	}
}

Clients die diesen Code nutzen möchten brauchen, ähnlich wie beim ersten HowTo um Schnittstellen, nur noch das Interface nutzen:

class ClientCode
{
	public IDataProvider provider;
}

Dadurch kann man theoretisch die Datenbank nun wechseln so oft man möchte – unser ClientCode benötigt keine Änderung.

Allerdings…

ist dieses Beispiel meiner Meinung nach sehr “dürftig”. Zwar kann man es daran sehr gut erklären, aber bei wie vielen Applikationen wird sowas überhaupt mit betrachtet? Bei vielen Projekten steht fest, welche DB genommen werden soll/genommen wird – die Datenbank wird sich sicherlich nicht ändern (und wenn doch, wird dies halt ein riesiger Change Request ;) ).

Was viele nicht betrachten ist, dass durch den Einsatz von Interfaces auch die Testbarkeit einer Applikation steigert.

Beispielanwendung:

image

Unser “Data” gibt Daten an den Service, dieser verarbeitet diese und reicht die weiter (3-Tier). Wie hier zu sehen ist, gibt es nur die konkrete Klasse “PersonService” (unser Service) und “Person” (unser Model) – alles andere sind Schnittstellen:

public class PersonService : IPersonService
    {
        private IAuthenticationService authentication;
        private IPersonDataProvider personProvider;

        public PersonService(IAuthenticationService authSrv, IPersonDataProvider provider)
        {
            authentication = authSrv;
            personProvider = provider;
        }

        public List<Person> GetPersons()
        {
            if (authentication.IsAuthenticated())
            {
                return personProvider.GetPersons();
            }

            return null;
        }

        public void AddPerson(Person p)
        {
            if (authentication.IsAuthenticated())
            {
                personProvider.AddPerson(p);
            }
        }

    }

Unser Service kennt nur die Interface vom AuthenticationServie und vom DataProvider – diese werden im Konstruktor mit übergeben.
Anmerkung: Diese Form der Übergabe von den “Abhängigkeiten” ist dem Feld der “Dependency Injection” zuzuordnen und werde ich in einem anderen HowTo genauer definieren.

Unser Service kann die Daten nur ausgeben, wenn wir authentifiziert sind.

Szenario:

Wenn wir nun ein größeres Projekt angehen und der Authentifizierungsmechanismus nicht so einfach ist und auch Fremdsysteme mit einbezieht, dann wäre es ja nett, wenn man während der Entwicklung eine Art “Fake” aufsetzen kann. Durch die Interface ist es sehr einfach, sowas zu machen:

image

In meinem UnitTest Projekt will ich meinen Service testen – und nur diesen(!). dafür habe ich mit ein “TestPersonDataProvider” erstellt, der “IPersonDataProvider” implementiert und jeweils ein AuthenticationService, welcher mir sagt, dass ich authentifiziert bin, oder nicht.

Mein Testcode:

    [TestClass]
    public class PersonServiceUnitTest
    {
        [TestMethod]
        public void PersonService_ReturnPersons_Work_With_Authentication()
        {
            TestPersonDataProvider provider = new TestPersonDataProvider();
            PersonService srv = new PersonService(new TrueTestAuthenticationService(), provider);
            Assert.AreEqual(provider.InMemoryPersonCollection.Count, srv.GetPersons().Count);
        }

        [TestMethod]
        public void PersonService_Should_Return_Null_Without_Authentication()
        {
            TestPersonDataProvider provider = new TestPersonDataProvider();
            PersonService srv = new PersonService(new FalseTestAuthenticationService(), provider);
            Assert.IsNull(srv.GetPersons());
        }
    }

In meinem Testcode kann ich nun einfach die jeweiligen “Fakes” übergeben – ohne den Clientcode ändern zu müssen.

Fazit:

Durch den Einsatz von Interfaces kann man sehr erweiterbare Systeme machen – ein Beispiel ist z.B. das MVC Framework von Microsoft. Der Nebeneffekt ist natürlich auch, dass man die Datenquelle wechseln kann oder dass man einfache Fakes in den Tests, aber auch im Produktionscode verwenden kann (jedenfalls bevor auf “Release” gedrückt wird ;) ).

Schnittstellen erlauben erweiterbare und testbare Software!

[ Download Democode ]


Kick It auf dotnet-kicks.de
Wenn dir der Blogpost gefallen hat, dann hinterlasse doch einen Kommentar. Wenn du auf dem Laufenden bleiben willst, abonniere unseren RSS Feed oder folge uns auf Twitter.

About the author

Written by Robert Mühsig

Robert Mühsig (@robert0muehsig) ist Webentwickler und beschäftigt sich mit Web-Frameworks (vor allem dem ASP.NET MVC Framework) und scheut sich auch nicht vor Javascript. Ansonsten bloggt er über all jene Probleme, die ihm über den Weg laufen. Seit 2008 ist er Microsoft MVP für ASP.NET und er arbeitet bei der T-Systems Multimedia Solutions GmbH in Dresden. Treffen kann man ihn online via Twitter (@robert0muehsig) oder dieser Seite oder bei der .NET User Group Dresden.

2 Responses

Comment on this post

Letzte Posts

  • Carriage Return / Neue Zeile in Textareas

    Eine kleine Aufgabe: Jede neue Textzeile (Carriage Return/Wenn man Enter drückt ) in einer Textarea soll ein Element in einer Auflistung sein – wie mach ich das jetzt am einfachsten? Eigentlich ein grundlegendes Element im Web und der Nutzer macht bewusst Absätze – daher wäre es nur gerecht, wenn man das auch entsprechend würdigt. Kleine ...

  • image.png
    Doom, Quake, Wolfenstein & co. Source Code auf GitHub

    id Software, die Macher von Doom, Quake, Wolfenstein & co., stellen regelmäßig ihre älteren Spieltitle als Open Source zur Verfügung. Das Ganze runterzuladen fand ich bisher immer recht mühselig, allerdings gibt es seit kurzer Zeit die Sourcen auch auf GitHub. Darunter Spiele wie Doom 3, Quake 3, Wolfenstein für iOS. Wer also schon immer mal ...

  • image.png
    Twitter Bootstrap 2.0 released & “Release Präsentation”

    Wie bereits vom Twitter Bootstrap Team angekündigt wurde offiziel die Version 2.0 des UI Toolskits “Twitter Bootstrap” veröffentlich. Zudem wurden die Slides, welche bei der Release Party gezeigt wurden auch veröffentlicht: Downloads finden sich auf der Twitter Bootstrap Seite auf GitHub. Wenn dir der Blogpost gefallen hat, dann hinterlasse doch einen Kommentar. Wenn du auf ...

  • image.png
    Javascript zu Dart Translator

    Dart, Google Javascript Alternative, wurde vor ein paar Monaten vorgestellt und die Webentwickler Szene ist noch etwas gespalten, ob Dart nun überflüssig ist oder einfach nur cool und längst überfällig ist. Um die Sprache näher zu erläutern hat Google die grundlegenden Javascript Basics nach Dart übersetzt. Das Ergebnis ist der “Translator”. Der Name mag momentan ...

  • Twitter Bootstrap 2.0–“Beta”

    Twitter Bootstrap, ein UI-Toolkit für Web-Applikationen von Twitter, erscheint (wie bereits berichtet) demnächst in der Version 2.0. Der offizielle Release ist am 31. Januar, allerdings beginnt jetzt laut Mark Otto (einer der Hauptentwickler von Twitter Bootstrap) die intensive Test-Phase. Das heisst, das es nun offiziel auch die 2.0 Dokumentation online gibt. Im Vergleich zur aktuellen ...

Support us!

Facebook