15.02.2013 Aufrufe

b2aat6n

b2aat6n

b2aat6n

MEHR ANZEIGEN
WENIGER ANZEIGEN

Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.

YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.

Stack und Queue implementieren<br />

Der Nächste bitte!<br />

Immer hübsch der Reihe nach: Das gilt nicht nur im Wartezimmer, sondern auch im Stack und in der Queue der<br />

Informatiker. Und wer sich das Entwicklerleben vereinfachen will, sollte auch bei ihrer Implementierung<br />

die richtige Reihenfolge einhalten: Erst planen, dann Tests entwickeln, dann implementieren.<br />

A<br />

ls Entwickler nehmen wir<br />

Stack und Queue, wie viele<br />

andere Datenstrukturen, als<br />

selbstverständlich hin, sind<br />

sie doch im .NET Framework enthalten.<br />

Weil ihre Funktionsweise so einfach ist, besteht<br />

sicherlich die Versuchung, direkt mit<br />

der Implementierung zu beginnen. Doch<br />

schon kommen die ersten Fragen um die<br />

Ecke: Wie soll der erste Test aussehen? Wie<br />

testet man einen Stack überhaupt, das<br />

heißt, kann man Push isoliert testen? Oder<br />

kann man Push nur testen, indem man Pop<br />

ebenfalls testet?<br />

Bevor Sie versuchen, diese Fragen zu beantworten,<br />

sollten Sie den Konsolendeckel<br />

besser erst mal schließen und zu einem<br />

Stück Papier greifen. Denn die zentrale Frage<br />

vor dem ersten Test lautet, wie denn die<br />

interne Repräsentation des Stacks überhaupt<br />

aussieht. Dies mit Papier und Bleistift<br />

zu planen ist einfacher, als es einfach<br />

so im Kopf zu tun. Dabei übersieht man<br />

schnell mal ein Detail.<br />

Ein Stack muss in der Lage sein, jeweils<br />

das oberste Element zu liefern. Das ist die<br />

Aufgabe der Pop-Methode. Nachdem das<br />

oberste Element geliefert wurde, muss der<br />

Stack beim nächsten Mal das nächste Element<br />

liefern, das also unmittelbar auf das<br />

oberste folgt. Daraus ergibt sich die Notwendigkeit,<br />

innerhalb des Stacks jeweils zu<br />

wissen, welches das oberste Element ist.<br />

Ferner muss zu jedem Element bekannt<br />

sein, welches das nächste Element ist. So ergibt<br />

sich eine ganz einfache interne Datenstruktur<br />

für den Stack, siehe Abbildung 1.<br />

Hat man diese Datenstruktur erst einmal<br />

aufgemalt, ist es ein Leichtes, sie in Code<br />

zu übersetzen. Aber Achtung, den Test<br />

nicht vergessen! Bei einem Stack bietet es<br />

sich an, zwei unterschiedliche Zustände zu<br />

betrachten:<br />

❚ einen leeren Stack,<br />

❚ einen nicht leeren Stack.<br />

In solchen Fällen erstelle ich für die unterschiedlichen<br />

Szenarien gerne getrennte<br />

[Abb. 1] top- und<br />

next-Zeiger im<br />

Testklassen. Dann kann nämlich das Setup<br />

des Tests dafür sorgen, dass das Szenario<br />

bereitgestellt wird. Die Testklasse, welche<br />

sich mit einem leeren Stack befasst, instanziert<br />

einfach einen neuen Stack. In der Testklasse<br />

zum Szenario „nicht leerer Stack“<br />

wird der Stack im Setup gleich mit ein paar<br />

Werten befüllt. So können sich die Tests jeweils<br />

auf das konkrete Szenario beziehen.<br />

Doch wie sieht nun der erste Test aus?<br />

Ich habe mich entschieden, den ersten Test<br />

zu einem leeren Stack zu erstellen. Es erscheint<br />

mir nicht sinnvoll, bei dem Szenario<br />

eines nicht leeren Stacks zu beginnen,<br />

weil dann vermutlich für den ersten Test<br />

bereits sehr viel Funktionalität implementiert<br />

werden muss. Ich möchte lieber in<br />

kleinen Schritten vorgehen, um sicher zu<br />

sein, dass ich wirklich nur gerade so viel<br />

Code schreibe, dass ein weiterer Test erfolgreich<br />

verläuft.<br />

Für den ersten Test zu einem leeren Stack<br />

überlege ich mir, wie die interne Repräsentation<br />

eines leeren Stacks aussehen soll, und<br />

komme zu dem Schluss, dass der Zeiger, der<br />

auf das erste Element verweist, null sein soll.<br />

Listing 1 zeigt den ersten Test. In der Setup-<br />

Methode der Testklasse wird ein leerer Stack<br />

für int-Elemente instanziert. Der erste Test<br />

prüft, ob dann der top-Zeiger gleich null ist.<br />

Nun wird vielleicht dem einen oder anderen<br />

der Einwand im Kopf herumkreisen,<br />

dass damit ja erstens die Sichtbarkeit der<br />

internen Repräsentation nach außen getragen<br />

wird und zweitens Interna getestet<br />

werden. Zur Sichtbarkeit sei gesagt, dass<br />

Listing 1<br />

LÖSUNG<br />

Einen leeren Stack testen.<br />

[TestFixture]<br />

public class Ein_leerer_Stack {<br />

private Stack sut;<br />

[SetUp]<br />

public void Setup() {<br />

sut = new Stack();<br />

}<br />

[Test]<br />

public void Hat_kein_top_Element() {<br />

Assert.That(sut.top, Is.Null);<br />

}<br />

}<br />

ich das Feld top auf internal setze. Damit<br />

ist es zunächst nur innerhalb der Assembly<br />

sichtbar, in der die Stack-Implementierung<br />

liegt. Durch ein Attribut in der Datei AssemblyInfo.cs<br />

wird die Sichtbarkeit dann<br />

dosiert erweitert auf die Testassembly. Das<br />

Attribut sieht wie folgt aus:<br />

[assembly:<br />

InternalsVisibleTo("stack.tests")]<br />

Damit kann nun auch aus der Testassembly<br />

auf die Interna zugegriffen werden.<br />

Und schon sind wir beim zweiten Einwand,<br />

dass nun diese Interna im Test verwendet<br />

werden. Das halte ich für vernachlässigbar.<br />

Sicher ist es erstrebenswert, Tests<br />

so zu schreiben, dass sie möglichst wenige<br />

Abhängigkeiten zu den Interna der Klasse<br />

haben. Denn wenn nur über die öffentliche<br />

Schnittstelle getestet wird, sind die Tests<br />

www.dotnetpro.de dotnetpro.dojos.2011 39<br />

Stack.<br />

Listing 2<br />

Datenstruktur für die Stack-<br />

Elemente.<br />

internal class Element {<br />

public TData Data { get; set; }<br />

public Element Next{get; set;}<br />

}

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!