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

  • image.png
    RavenHQ–RavenDB in der Cloud

    Ayende Rahien hat es heute verkündet – RavenHQ, der RavenDB Cloud Hoster (natürlich von und mit Ayende) ist ab heute raus aus der Beta und man kann es von überall aus nutzen. In der Betaphase waren nur Nutzer von AppHarbor zugelassen. Was ist RavenHQ? RavenHQ ist im Grunde ein gehostes RavenDB in den Rechenzentren von ...

  • image.png
    GitHub for Windows–erste Eindrücke

    Git ist schon eine tolle Sachen und eröffnet viele neue Möglichkeiten – allerdings ist der Einstieg recht hart und selbst wenn man die guten Hilfsanleitungen auf GitHub befolgt, kommt man am Anfang nur langsam vorwärt. Insbesondere ist das Tooling für Windows / .NET Entwickler auch nicht gerade “bekanntes Terrain”. GitHub to the rescue! Die GitHub ...

  • image.png
    Chocolatey–apt-get für Windows

    Durch Zufall bin ich auf das Tool “Chocolatey” gestoßen. Wer die Website sich anschaut, wird evtl. eine Verwandschaft mit NuGet ausmachen. Was macht Chocolatey? Chocolatey ist ein “Maschine Package Manager”, das bedeutet, dass man für seine Maschine einfach Tools runterladen und Updaten kann – direkt über die Konsole. Was ist der Unterschied zu NuGet? NuGet ...

  • image.png
    SASS, LESS & Coffeescript in Visual Studio mit der Web Workbench

    CSS und Javascript sind die “kleinste” Schnittmenge von allen Browsern für die Erstellung von Web-Applikationen. Leider geht dabei etwas komfort verloren, daher lieben alle Webentwickler jQuery! SASS und LESS sind zwei Varianten, wie man “schöner” CSS schreiben kann und Coffeescript versucht Javascript Entwicklung zu vereinfachen. Aber immer der Reihe nach… Was ist SASS? SASS steht ...

  • image.png
    Code-Inside Sample nun auf GitHub: Google Code zu GitHub Migration

    Seit einiger Zeit habe ich Beispielcode auf Google Code bereitgestellt. Einfach nur noch weg von Google Code O-Ton damals war: Ich hatte mich für Google Code entschieden, weil ich hoffe dass früher oder später die Google Code Suche nutzbar ist und es dadurch wenigstens ein kleiner Mehrwert entsteht. Allerdings wirft es momentan noch ein Fehler. ...

Auf Amazon einkaufen & unterstützen

Facebook