Stefan Lieser, Tilman Börner. Dojos für Entwickler. 15 Aufgaben und Lösungen in.net

Größe: px
Ab Seite anzeigen:

Download "Stefan Lieser, Tilman Börner. Dojos für Entwickler. 15 Aufgaben und Lösungen in.net"

Transkript

1 #1 Stefan Lieser, Tilman Börner Dojos für Entwickler 15 Aufgaben und Lösungen in.net

2 EINLEITUNG Wer übt, gewinnt E in Profimusiker übt täglich mehrere Stunden. Er übt Fingerfertigkeit, Phrasierung, Ansatz beziehungsweise Haltung, Intonation und VomBlatt-Spielen. Als Hilfsmittel verwendet er Tonleitern, Etüden, Ausschnitte von Stücken und Unbekanntes. Ohne Üben könnte er die Qualität seines Spiels nicht halten, geschweige denn verbessern. Üben gehört für ihn dazu. Wie sieht das bei Ihnen und der Programmiererei aus? Sie sind doch auch Profi. Nicht in der Musik, aber doch beim Codieren an der Computertastatur. Üben Sie auch? Gemeint ist nicht die Aufführung, sprich das Program-mieren, mit dem Sie sich Ihr Einkommen verdienen. Gemeint sind die Etüden, das Üben von Fingerfertigkeit, Intonation, Ansatz und Vom-Blatt-Spielen. Wie sehen diese Aufgaben denn bei einem Programmierer aus? Freilich ließe sich die Analogie bis zum Abwinken auslegen. Hier mag ein kleiner Ausschnitt genügen: Sie könnten als Etüde zum Beispiel trainieren, dass Sie immer erst den Test schreiben und dann die Implementation der Methode, die den Test erfüllt. Damit verwenden Sie künftig nicht immer wieder den falschen Fingersatz, sondern immer gleich die richtige Reihenfolge: Test Implementation. Klar, Üben ist zeitraubend und manchmal nervtötend vor allem für die, die zuhören. Aber Üben kann auch Spaß machen. Kniffeln, eine Aufgabe lösen und dann die eigene Lösung mit einer anderen Lösung vergleichen. Das ist der Grundgedanke beim dotnetpro.dojo. In jeder Ausgabe stellt dotnetpro eine Aufgabe, die in maximal drei Stunden zu lösen sein sollte. Sie investieren einmal pro Monat wenige Stunden und ge- dotnetpro.dojos.2011 winnen dabei jede Menge Wissen und Erfahrung. Den Begriff Dojo hat die dotnetpro nicht erfunden. Dojo nennen die Anhänger fernöstlicher Kampfsportarten ihren Übungsraum. Aber auch in der Programmierung hat sich der Begriff eines Code Dojo für eine Übung eingebürgert. Das können Sie gewinnen Der Gewinn lässt sich in ein Wort fassen: Lernen. Das ist Sinn und Zweck eines Dojo. Sie können/ dürfen/sollen lernen. Einen materiellen Preis loben wir nicht aus. Ein dot-netpro.dojo ist kein Contest. Dafür gilt aber : Falsche Lösungen gibt es nicht. Es gibt möglicherweise elegantere, kürzere oder schnellere, aber keine falschen. Wichtig ist, dass Sie reflektieren, was Sie gemacht haben. Das können Sie, indem Sie Ihre Lösung mit der vergleichen, die Sie eine Ausgabe später in der dotnetpro finden. Wer stellt die Aufgabe? Wer liefert die Lösung? Die kurze Antwort lautet: Stefan Lieser. Die lange Antwort lautet: Stefan Lieser, seines Zeichens Mitinitiator der Clean Code Deve-loper Initiative. Stefan ist freiberuflicher Trainer und Berater und Fan von intelligenten Entwicklungsmethoden, die für Qualität der resultierenden Software sorgen. Er denkt sich die Aufgaben aus und gibt dann auch seine Lösung zum Besten. Er wird auch mitteilen, wie lange er gebraucht und wie viele Tests er geschrieben hat. Das dient wie oben schon gesagt nur als Anhaltspunkt. Falsche Lösungen gibt es nicht. Der Spruch Übung macht den Meister ist abgedroschen, weil oft bemüht, weil einfach richtig. Deshalb finden Sie in diesem Sonderheft 15 dotnetpro.dojos, also Übungsaufgaben inklusive einer Musterlösung und Grundlagen. 3

3 INHALT 15 Aufgaben und Lösungen 5 Aufgabe 1:Vier gewinnt Ein Spielfeld, zwei Spieler und jede Menge Spaß beim Programmieren: Das kleine Brettspiel ist genau das Richtige zum Warmwerden. 66 Aufgabe 12: Twitter Es treten auf: mehrere Threads, eine Synchronisation, ein Timer, ein Control wahlweise in WPF-, Windows-Forms- oder Silverlight-Qualität und ein API. Fertig ist das Twitter-Band. 9 Aufgabe 2:Data Binding Knüpfe Kontrollelement an Eigenschaft, und schon wirkt der Zauber: Veränderungen der Eigenschaft spiegeln sich im Control wider und auch andersherum. 71 Aufgabe 13:Graphen Entwerfen Sie ein API für den Umgang mit gerichteten Graphen, implementieren Sie die Datenstruktur und einen beliebigen Algorithmus dazu, wie etwa topologische Sortierung. Und los. 14 Aufgabe 3:Testdatengenerator Meier, Müller, Schulze ganze Mal: Für einen Testdatengenerator ist das eine Sache von Sekunden. Aber wie baut man einen solchen? 22 Aufgabe 4:Mogeln mit EVA Statt Rein-Raus-Kaninchentechnik die Eingabe, Verarbeitung, Ausgabe: modernste Technik im Dienst des Mogelns beim Minesweeper-Spiel. Na super. 26 Aufgabe 5:Boxplot Packen Sie den Sandsack wieder weg: nicht Box, platt, sondern Boxplot: Diese spezielle Grafikform zeigt kleinsten und größten Wert, Mittelwert und die Quartile. 31 Aufgabe 6:RavenDB Computer aus, Daten weg? Von wegen: Eine Persistenzschicht sorgt für deren Überleben. Mit RavenDB braucht man dafür auch keinen SQL-Server. 38 Aufgabe 7:Stack und Queue Wie bitte? Stack und Queue bietet doch das.net Framework. Stimmt. Aber die Selbstimplementierung bringt viel Selbsterkenntnis. Sie werden es sehen. 44 Aufgabe 8:Windows-Dienst Er arbeitet im Verborgenen, im Untergrund. Ist aber so wichtig, dass auf ihn nicht verzichtet werden kann. Bauen Sie doch mal einen. 50 Aufgabe 9:Event-Based Components Was, bitte schön, hat Silbentrennung mit EBC zu tun? Erst einmal gar nichts. Es sei denn, die Aufgabe lautet: Baue Silbentrennservice mit EBCs. 77 Aufgabe 14:ToDo, MVVM und Datenfluss Am Ende haben Sie eine nützliche ToDo-Listen-Anwendung. Am Anfang haben Sie ein Problem: Wie modellieren Sie die Softwarearchitektur? Aber nur Mut: Auch das klappt. 87 Aufgabe 15:ToDo und die Cloud Die ToDo-Listen-Anwendung soll jetzt noch richtig cool werden: durch eine Synchronisation über die Cloud. Ein bisschen Hirnschmalz... Grundlagen 82 MVVM und EBC Model View ViewModel und Event-Based Components: Das sind zwei aktuelle Technologien, die sich aber gut miteinander kombinieren lassen. Stefan Lieser zeigt, wie das geht. 56 Aufgabe 10:ITree<T> Ich bau nen Baum für dich. Aus Wurzel, Zweig und Blatt und den Interfaces ITree<T> und INode<T>. Und Sie dürfen ihn erklettern. 95 Klassische Katas Sie heißen Kata Potter, Kata BankOCR oder Kata FizzBuzz: An klassischen Programmieraufgaben gibt es inzwischen schon ganze Kataloge. Tilman Börner stellt die wichtigsten vor. 61 Aufgabe 11:LINQ Frage: Wie heißt die bekannteste Abfragesprache? Richtig: SQL. Aber in dieser Aufgabe geht es um eine andere: Language Integrated Query. Impressum 94 Impressum 4 dotnetpro.dojos

4 AUFGABE Stefan, vielleicht sollten wir erst einmal mit etwas Einfacherem anfangen. Vielleicht wäre ein kleines Spiel zum Warmwerden genau das Richtige. Fällt dir dazu eine Aufgabe ein? K lar, können wir machen. Wie wäre es beispielsweise mit dem Spiel 4 gewinnt? Bei dieser Aufgabe geht es vor allem um eine geeignete Architektur und die Implementierung der Logik und nicht so sehr um eine schicke Benutzeroberfläche. 4 gewinnt wird mit einem aufrecht stehenden Spielfeld von sieben Spalten gespielt. In jede Spalte können von oben maximal sechs Spielsteine geworfen werden. Ein Spielstein fällt nach unten, bis er entweder auf den Boden trifft, wenn es der erste Stein in der Spalte ist, oder auf den schon in der Spalte liegenden Steinen zu liegen kommt. Die beiden Spieler legen ihre gelben beziehungsweise roten Spielsteine abwechselnd in das Spielfeld. Gewonnen hat der Spieler, der zuerst vier Steine direkt übereinander, nebeneinander oder diagonal im Spielfeld platzieren konnte. Implementieren Sie ein Spiel Ein Spiel, das zwei Spieler gegeneinander spielen. Die Implementierung soll die Spielregeln überwachen. So soll angezeigt werden, welcher Spieler am Zug ist (Rot oder Gelb). Ferner soll angezeigt werden, ob ein Spieler gewonnen hat. Diese Auswertung erfolgt nach jedem Zug, sodass nach jedem Zug angezeigt wird, entweder welcher Spieler an der Reihe ist oder wer gewonnen hat. Hat ein Spieler gewonnen, ist das Spiel zu Ende und kann neu gestartet werden. Damit es unter den Spielern keinen Streit gibt, werden die Steine, die zum Gewinn führten, ermittelt. Bei einer grafischen Benutzeroberfläche könnten die vier Steine dazu farblich markiert oder eingerahmt werden. Bei einer Konsolenoberfläche können die Koordinaten der Steine ausgegeben werden. Die Bedienung der Anwendung erfolgt so, dass der Spieler, der am Zug ist, die Spalte angibt, in die er einen Stein werfen will. Dazu sind die Spalten von eins bis sieben nummeriert. Bei einer grafischen Benutzeroberfläche können die Spalten je durch einen Button gewählt werden. Wird das Spiel als Konsolenanwendung implementiert, genügt die Eingabe der jeweiligen Spaltennummer per Tastatur. Die Abbildungen 1 und 2 zeigen, wie eine Oberfläche aussehen könnte. Ist die Spalte, in die der Spieler seinen Stein legen möchte, bereits ganz mit Steinen gefüllt, erfolgt eine Fehlermeldung, und der Spieler muss erneut einen Spielstein platzieren. Programmieraufgabe Die Programmieraufgabe lautet, ein Spiel 4 gewinnt zu implementieren. Dabei liegt der Schwerpunkt auf dem Entwurf einer angemessenen Architektur, der Implementierung der Spiellogik und zugehörigen automatisierten Tests. Die Benutzerschnittstelle des Spiels steht eher im Hintergrund. Ob animierte WPF- Oberfläche, WinForms, ASP.NET oder Konsolenanwendung, das ist nicht wichtig. Im Vordergrund soll eine Lösung stehen, die leicht in eine beliebige Oberflächentechnologie integriert werden kann. Evolvierbarkeit und Korrektheit sollen hier also stärker bewertet werden als eine superschicke Oberfläche. Im nächsten Heft zeigen wir eine exemplarische Musterlösung. Die Lösung kann es in einem solchen Fall bekanntlich eh nicht geben. Damit möchte ich Sie, lieber Leser, noch mal ermutigen, sich der Aufgabe anzunehmen. Investieren Sie etwas Zeit, und erarbeiten Sie eine eigene Lösung. Die können Sie dann später mit der hier vorgestellten vergleichen. Viel Spaß! [Abb. 1 und 2] Eine mögliche Oberfläche (links) und die Anzeige der siegreichen vier Steine (rechts). Aber auf die Oberfläche kommt es bei dieser Übung nicht an. Wer übt, gewinnt In jeder dotnetpro finden Sie eine Übungsaufgabe von Stefan Lieser, die in maximal drei Stunden zu lösen sein sollte. Wer die Zeit investiert, gewinnt in jedem Fall wenn auch keine materiellen Dinge, so doch Erfahrung und Wissen. Es gilt: Falsche Lösungen gibt es nicht. Es gibt möglicherweise elegantere, kürzere oder schnellere Lösungen, aber keine falschen. Wichtig ist, dass Sie reflektieren, was Sie gemacht haben. Das können Sie, indem Sie Ihre Lösung mit der vergleichen, die Sie eine Ausgabe später in der dotnetpro finden. Übung macht den Meister. Also los geht s. Aber Sie wollten doch nicht etwa sofort Visual Studio starten dotnetpro.dojos

5 Eine Übung, bei der Sie nur gewinnen konnten Vier gewinnt. Eine Lösung. Die Aufgabe war, das Spiel Vier gewinnt zu implementieren. Auf den ersten Blick ist das eine eher leichte Übung. Erst bei genauerem Hinsehen erkennt man die Schwierigkeiten. Wie zerlegt man beispielsweise die Aufgabenstellung, um überschaubare Codeeinheiten zu erhalten? Leser, die sich der Aufgabe an - genommen haben, ein Vier-gewinnt-Spiel zu implementieren [1], werden es gemerkt haben: Der Teufel steckt im Detail. Der Umgang mit dem Spielfeld, das Erkennen von Vierergruppen, wo soll man nur anfangen? Wer zu früh gezuckt hat und sofort mit der Codeeingabe begonnen hat, wird es vielleicht gemerkt haben: Die Aufgabe läuft aus dem Ruder, wächst einem über den Kopf. Das ging mir nicht anders. Früher. Heute setze ich mich erst mit einem Blatt Papier hin, bevor ich beginne, Code zu schreiben. Denn die erste Herausforderung besteht nicht darin, das Problem zu lösen, sondern es zu verstehen. Beim Vier-gewinnt-Spiel war eine Anforderung bewusst ausgeklammert: die Benutzerschnittstelle. In der Aufgabe geht es um die Logik des Spiels. Am Ende soll demnach eine Assembly entstehen, in der die Spiellogik enthalten ist. Diese kann dann in einer beliebigen Benutzerschnittstelle verwendet werden. Beim Spiel selbst hilft es, sich die Regeln vor Augen zu führen. Zwei Spieler legen abwechselnd gelbe und rote Spielsteine in ein 7 x 6 Felder großes Spielfeld. Derjenige, der als Erster vier Steine seiner Farbe nebenein - ander liegen hat, hat das Spiel gewonnen. Hier hilft es, sich mögliche Vierergruppen aufzumalen, um zu erkennen, welche Konstellationen im Spielfeld auftreten können. Nachdem ich das Problem durchdrungen habe, zeichnet sich eine algorithmische Lösung ab. Erst jetzt beginne ich, die gesamte Aufgabenstellung in Funktionseinheiten zu zerlegen. Ich lasse zu diesem Zeitpunkt ganz bewusst offen, ob eine Funktionseinheit am Ende eine Methode, Klasse oder Komponente ist. Wichtig ist erst einmal, dass jede Funktionseinheit eine klar definierte Aufgabe hat. Hat sie mehr als eine Aufgabe, zerlege ich sie in mehrere Funktionseinheiten. Stellt man sich die Funktionseinheiten als Baum vor, in dem die Abhängigkeiten die verschiedenen Einheiten verbinden, dann steht auf oberster Ebene das gesamte Spiel. Es zerfällt in weitere Funktionseinheiten, die eine Ebene tiefer angesiedelt sind. Diese können wiederum zerlegt werden. Bei der Zerlegung können zwei unterschiedliche Fälle betrachtet werden: vertikale Zerlegung, horizontale Zerlegung. Der Wurzelknoten des Baums ist das gesamte Spiel. Diese Funktionseinheit ist jedoch zu komplex, um sie in einem Rutsch zu implementieren. Also wird sie zerlegt. Durch die Zerlegung entsteht eine weitere Ebene im Baum. Dieses Vorgehen bezeichne ich daher als vertikale Zerlegung. Kümmert sich eine Funktionseinheit um mehr als eine Sache, wird sie horizontal zerlegt. Wäre es beispielsweise möglich, einen Spielzustand in eine Datei zu speichern, könnte das Speichern im ersten Schritt in der Funktionseinheit Spiellogik angesiedelt sein. Dann stellt man jedoch fest, dass diese Funktionseinheit für mehr als eine Verantwortlichkeit zuständig wäre, und zieht das Speichern heraus in eine eigene Funktionseinheit. Dies bezeichne ich als horizontale Zerlegung. Erst wenn die Funktionseinheiten hinreichend klein sind, kann ich mir Gedanken darum machen, wie ich sie implementiere. Im Falle des Vier-gewinnt-Spiels zerfällt das Problem in die eigentliche Spiellogik und die Benutzerschnittstelle. Die Benutzerschnittstelle muss in diesem Fall nicht weiter zerlegt werden. Das mag in komplexen Anwendungen auch mal anders sein. Diese erste Zerlegung der Gesamtaufgabe zeigt Abbildung 1. Die Spiellogik ist mir als Problem noch zu groß, daher zerlege ich diese Funktions - einheit weiter. Dies ist eine vertikale Zerlegung, es entsteht eine weitere Ebene im Baum. Die Spiellogik zerfällt in die Spielregeln und den aktuellen Zustand des Spiels. Die Zerlegung ist in Abbildung 2 dargestellt. Die Spielregeln sagen zum Beispiel aus, wer das Spiel beginnt, wer den nächsten Zug machen darf et cetera. Der Zustand des Spiels wird beim echten Spiel durch das Spielfeld abgebildet. Darin liegen die schon gespielten Steine. Aus dem Spielfeld geht jedoch nicht hervor, wer als Nächster am Zug ist. Für die Einhaltung der Spielregeln sind beim echten Spiel die beiden Spieler verantwortlich, in meiner Implementierung ist es die Funktionseinheit Spielregeln. Ein weiterer Aspekt des Spielzustands ist die Frage, ob bereits vier Steine den Regeln entsprechend zusammen liegen, sodass ein Spieler gewonnen hat. Ferner birgt der Spielzustand das Problem, wohin der nächste gelegte Stein fällt. Dabei bestimmt der Spieler die Spalte und der Zustand des Spielbretts die Zeile: Liegen bereits Steine in der Spalte, wird der neue Spielstein zuoberst auf die schon vorhandenen gelegt. Damit unterteilt sich die Problematik des Spielzustands in die drei Teilaspekte Steine legen, nachhalten, wo bereits Steine liegen, erkennen, ob vier Steine zusammen liegen. Vom Problem zur Lösung Nun wollen Sie sicher so langsam auch mal Code sehen. Doch vorher muss noch geklärt werden, was aus den einzelnen Funktionseinheiten werden soll. Werden sie jeweils eine Klasse? Eher nicht, denn dann wären Spiellogik und Benutzerschnittstelle nicht ausreichend getrennt. Somit werden Benutzerschnittstelle und Spiellogik mindestens eigenständige Komponenten. Die Funktionseinheiten innerhalb der Spiel - logik hängen sehr eng zusammen. Alle leisten einen Beitrag zur Logik. Ferner scheint mir die Spiellogik auch nicht komplex genug, um sie weiter aufzuteilen. Es bleibt also bei den beiden Komponenten Benutzerschnittstelle und Spiellogik. Um beide zu einem lauffähigen Programm zusammenzusetzen, brauchen wir noch ein weiteres Projekt. Seine Aufgabe ist es, eine EXE-Datei zu erstellen, in der die beiden 6 dotnetpro.dojos

6 Komponenten zusammengeführt werden. So entstehen am Ende drei Komponenten. Abbildung 3 zeigt die Solution für die Spiellogik. Sie enthält zwei Projekte: eines für die Tests, ein weiteres für die Implemen - tierung. Die Funktionseinheit Spielzustand zerfällt in drei Teile. Beginnen wir mit dem Legen von Steinen. Beim Legen eines Steins in das Spielfeld wird die Spalte angegeben, in die der Stein gelegt werden soll. Dabei sind drei Fälle zu unterscheiden: Die Spalte ist leer, enthält schon Steine oder ist bereits voll. Es ist naheliegend, das Spielfeld als zweidimensionales Array zu modellieren. Jede Zelle des Arrays gibt an, ob dort ein gelber, ein roter oder gar kein Stein liegt. Der erste Index des Arrays bezeichnet dabei die Spalte, der zweite die Zeile. Beim Platzieren eines Steins muss also der höchste Zeilenindex innerhalb der Spalte ermittelt werden. Ist dabei das Maximum noch nicht erreicht, kann der Stein platziert werden. Bleibt noch eine Frage: Wie ist damit umzugehen, wenn ein Spieler versucht, einen Stein in eine bereits gefüllte Spalte zu legen? Eine Möglichkeit wäre: Sie stellen eine Methode bereit, die vor dem Platzieren eines Steins aufgerufen werden kann, um zu ermitteln, ob dies in der betreffenden Spalte möglich ist. Der Code sähe dann ungefähr so aus: if(spiel.kannplatzieren(3)) { spiel.legesteininspalte(3); Dabei gibt der Parameter den Index der Spalte an, in die der Stein platziert werden soll. Das Problem mit diesem Code ist, dass er gegen das Prinzip Tell don t ask verstößt. Als Verwender der Funktionseinheit, die das Spielbrett realisiert, bin ich gezwungen, das API korrekt zu bedienen. Bevor ein Spielstein mit LegeSteinInSpalte() in das Spielbrett gelegt wird, müsste mit KannPlatzieren() geprüft werden, ob dies überhaupt möglich ist. Nach dem Tell don t ask -Prinzip sollte man Klassen so erstellen, dass man den Objekten der Klasse mitteilt, was zu tun ist statt vorher nachfragen zu müssen, ob man eine bestimmte Methode aufrufen darf. Im Übrigen bleibt bei der Methode LegeSteinInSpalte() das Problem bestehen: Was soll passieren, wenn die Spalte bereits voll ist? Eine andere Variante könnte sein, die Methode LegeSteinInSpalte() mit einem Rückgabewert auszustatten. War das Platzieren erfolgreich, wird true geliefert, ist die Spalte bereits voll, wird false geliefert. In [Abb. 1] Die Aufgabe in Teile zerlegen: erster Schritt... [Abb. 2]... und zweiter Schritt. dem Fall müsste sich der Verwender der Methode mit dem Rückgabewert befassen. Am Ende soll der Versuch, einen Stein in eine bereits gefüllte Spalte zu platzieren, dem Benutzer gemeldet werden. Also müsste der Rückgabewert bis in die Benutzerschnittstelle transportiert werden, um dort beispielsweise eine Messagebox anzuzeigen. Die Idee, die Methode mit einem Rückgabewert auszustatten, verstößt jedoch ebenfalls gegen ein Prinzip, nämlich die Command/Query Separation. Dieses Prinzip besagt, dass eine Methode entweder ein Command oder eine Query sein sollte, aber nicht beides. Dabei ist ein Command eine Methode, die den Zustand des Objekts verändert. Für die Methode Lege- Stein InSpalte() trifft dies zu: Der Zustand des Spielbretts ändert sich dadurch. Eine Query ist dagegen eine Methode, die eine Abfrage über den Zustand des Objekts enthält und dabei den Zustand nicht verändert. Würde die Methode LegeSteinInSpalte() einen Rückgabewert haben, wäre sie dadurch gleichzeitig eine Query. Nach diesen Überlegungen bleibt nur eine Variante übrig: Die Methode LegeStein - InSpalte() sollte eine Ausnahme auslösen, wenn das Platzieren nicht möglich ist. Die Ausnahme kann in der Benutzerschnittstelle abgefangen und dort in einer entsprechenden Meldung angezeigt werden. Damit entfällt die Notwendigkeit, einen Rückgabewert aus der Spiellogik bis in die Benutzerschnittstelle zu transportieren. Ferner sind die Prinzipien Tell don t ask und Command/Query Separation eingehalten. Vier Steine finden [Abb. 3] Aufbau der Solution. Nun sind mit dem zweidimensionalen Array und der Methode LegeSteinInSpalte() bereits zwei Teilprobleme des Spielzustands gelöst: Im zweidimensionalen Array ist der Zustand des Spielbretts hinterlegt, und die Methode LegeSteinInSpalte() realisiert die Platzierungslogik. Das dritte Problem ist die Erkennung von Vierergruppen, also eines Gewinners. Vier zusammenhängende Steine können beim Vier-gewinnt-Spiel in vier Varianten auftreten: horizontal, vertikal, diagonal nach oben, diagonal nach unten. Diese vier Varianten gilt es zu implementieren. Dabei ist wichtig zu beachten, dass die vier Steine unmittelbar zusammen liegen müssen, es darf sich also kein gegnerischer Stein dazwischen befinden. Ich habe zuerst versucht, diese Vierergruppenerkennung direkt auf dem zwei - dimensionalen Array zu lösen. Dabei habe ich festgestellt, dass das Problem in zwei Teilprobleme zerlegt werden kann: Ermitteln der Indizes benachbarter Felder. Prüfung, ob vier benachbarte Felder mit Steinen gleicher Farbe besetzt sind. Für das Ermitteln der Indizes habe ich daher jeweils eigene Klassen implementiert, welche die Logik der benachbarten Indizes enthalten. Eine solche Vierergruppe wird mit einem Startindex instanziert und liefert dann die Indizes der vier benachbarten Felder. Diese Vierergruppen werden anschließend verwendet, um im Spielfeld zu ermitteln, ob die betreffenden Felder alle dotnetpro.dojos

7 Listing 1 Vierergruppe ermitteln. internal struct HorizontalerVierer : IVierer { private readonly int x; private readonly int y; public HorizontalerVierer(int x, int y) { this.x = x; this.y = y; public Koordinate Eins { get { return new Koordinate(x, y); public Koordinate Zwei { get { return new Koordinate(x + 1, y); public Koordinate Drei { get { return new Koordinate(x + 2, y); public Koordinate Vier { get { return new Koordinate(x + 3, y); public override string ToString() { return string.format("horizontal X: {0, Y: {1", x, y); Steine derselben Farbe enthalten. Die betreffenden Klassen heißen HorizontalerVierer, VertikalerVierer, DiagonalHochVierer und DiagonalRunterVierer. Listing 1 zeigt exemplarisch die Klasse HorizontalerVierer. Zunächst fällt auf, dass die Klasse internal ist. Sie wird im Rahmen der Spiellogik nur intern benötigt, daher soll sie nicht außerhalb der Komponente sichtbar sein. Damit Unit-Tests für die Klasse möglich sind, habe ich auf der Assembly das Attribut InternalsVisibleTo gesetzt. Dadurch kann die Assembly, welche die Tests enthält, auf die internen Details zugreifen. Aufgabe der Klasse HorizontalerVierer ist es, vier Koordinaten zu horizontal neben - einander liegenden Spielfeldern zu liefern. Dies erfolgt in den Properties Eins, Zwei, Drei und Vier. Dort werden jeweils die Indizes ermittelt. Das Ermitteln eines Gewinners geschieht anschließend in einem Flow aus zwei Schritten. Im ersten Schritt wird aus einem Spielfeld die Liste der möglichen Vierergruppen bestimmt. Im zweiten Schritt wird aus dem Spielfeld und den möglichen Vierergruppen ermittelt, ob eine der Vierergruppen Steine derselben Farbe enthält. Die beiden Schritte des Flows sind als Extension Methods realisiert. Dadurch sind sie leicht isoliert zu testen. Anschließend können sie hintereinander ausgeführt, also als Flow zusammengeschaltet werden: var gewinnervierer = spielfeld.allevierer().selbefarbe(spielfeld); Der Flow wird an zwei Stellen verwendet: zum einen beim Ermitteln des Gewinners, zum anderen, um zu bestimmen, welche Steine zum Sieg geführt haben. Da die Methode AlleVierer() ein IEnumerable liefert und SelbeFarbe() dies als ersten Parameter erwartet, können die beiden Extension Methods hintereinander geschrieben werden. Da das Spielfeld in beiden Methoden benötigt wird, verfügt Selbe - Farbe() über zwei Parameter. Das Ermitteln von vier jeweils neben - einander liegenden Feldern übernimmt die Methode AlleVierer(). Ein kurzer Ausschnitt zeigt die Arbeitsweise: internal static IEnumerable<IVierer> AlleVierer(this int[,] feld) { for (var x = 0; x <= feld.getlength(0) - 4; x++) { for (var y = 0; y < feld.getlength(1); y++) { yield return new HorizontalerVierer(x, y); // Ebenso für Vertikal und Diagonal Auch diese Methode ist internal, da sie außerhalb der Komponente nicht benötigt wird. In zwei geschachtelten Schleifen werden die Anfangsindizes von horizontalen Vierergruppen ermittelt. Für jeden Anfangsindex wird mit yield return eine Instanz eines HorizontalerVierers geliefert. Dieser übernimmt das Ermitteln der drei anderen Indizes. Eine Alternative zur gezeigten Methode wäre, die möglichen Vierer als Konstanten zu hinterlegen. Es würde dann die Berechnung in AlleVierer() entfallen, ferner die Listing 2 Prüfen auf gleiche Farben. Klassen HorizontalerVierer et cetera. Ob die Felder einer Vierergruppe alle mit Steinen der gleichen Farbe besetzt sind, analysiert die Methode SelbeFarbe(). Durch die Verwendung der Klassen HorizontalerVierer et cetera ist dies einfach: Jeder Vierer liefert seine vier Koordinaten. Damit muss nur noch im Spielfeld nachgesehen werden, ob sich an allen vier Koordinaten Steine gleicher Farbe befinden, siehe Listing 2. Am Ende müssen die einzelnen Funk - tionseinheiten nur noch gemeinsam verwendet werden. Die dafür verantwortliche Klasse heißt VierGewinntSpiel. Sie ist public und repräsentiert nach außen die Komponente. Die Klasse ist für die Spiel - regeln zuständig. Da das abwechselnde Ziehen so einfach ist, habe ich mich entschlossen, diese Logik nicht auszulagern. In der Methode LegeSteinInSpalte(int spalte) wird der Zustand des Spiels aktualisiert. Dies geht ansatzweise wie folgt: if (Zustand == Zustaende.RotIstAmZug) { spielbrett.spielestein(spieler.rot, spalte); Zustand = Zustaende.GelbIstAmZug; Es wird also ein entsprechender Spielstein gelegt und anschließend ermittelt, wer am Zug ist. Etwas später folgt dann die Auswertung eines möglichen Gewinners: if (spielbrett.gewinner == Spieler.Rot){ Zustand = Zustaende.RotHatGewonnen; Die Ermittlung eines Gewinners erfolgt also im Spielbrett, während hier nur der Zustand des Spiels verwaltet wird. Fazit: Die richtigen Vorüberlegungen sind der Schlüssel zu einer erfolgreichen Implementierung. [ml] [1] Stefan Lieser, Wer übt, gewinnt, dotnetpro 3/2010, Seite 118 f., internal static IEnumerable<IVierer> SelbeFarbe(this IEnumerable<IVierer> vierer, int[,] feld) { foreach (var vier in vierer) { if ((feld[vier.eins.x, vier.eins.y]!= 0) && (feld[vier.eins.x, vier.eins.y] == feld[vier.zwei.x, vier.zwei.y]) && (feld[vier.eins.x, vier.eins.y] == feld[vier.drei.x, vier.drei.y]) && (feld[vier.eins.x, vier.eins.y] == feld[vier.vier.x, vier.vier.y])) { yield return vier; 8 dotnetpro.dojos

8 AUFGABE INotifyPropertyChanged-Logik automatisiert testen Zauberwort DataBinding ist eine tolle Sache: Objekt an Formular binden und wie von Zauberhand stellen die Controls die Eigenschaftswerte des Objekts dar. DataBinding ist aber auch knifflig. Stefan, kannst du dazu eine Aufgabe stellen? DataBinding ist beliebt. Lästig daran ist: Man muss die INotifyPropertyChanged-Schnittstelle implementieren. Sie fordert, dass bei Änderungen an den Eigenschaften eines Objekts das Ereignis PropertyChanged ausgelöst wird. Dabei muss dem Ereignis der Name der geänderten Eigenschaft als Parameter in Form einer Zeichenkette übergeben werden. Die Frage, die uns diesmal beim dotnetpro.dojo interessiert, ist: Wie kann man die Implementierung der INotifyPropertyChanged-Schnittstelle automatisiert testen? Die Funktionsweise des Events für eine einzelne Eigenschaft zu prüfen ist nicht schwer. Man bindet einen Delegate an den Property- Changed-Event und prüft, ob er bei Änderung der Eigenschaft aufgerufen wird. Außerdem ist zu prüfen, ob der übergebene Name der Eigenschaft korrekt ist, siehe Listing 3. Um zu prüfen, ob der Delegate aufgerufen wurde, erhöhen Sie im Delegate beispielsweise eine Variable, die außerhalb definiert ist. Durch diesen Seiteneffekt können Sie überprüfen, ob der Event beim Ändern der Eigenschaft ausgelöst und dadurch der Delegate aufgerufen wurde. Den Namen der Eigenschaft prüfen Sie innerhalb des Delegates mit einem Assert. Solche Tests für jede Eigenschaft und jede Klasse, die INotifyPropertyChanged implementiert, zu schreiben, wäre keine Lösung, weil Sie dabei Code wiederholen würden. Da die Eigenschaften einer Klasse per Reflection ermittelt werden können, ist es nicht schwer, den Testcode so zu verallgemeinern, dass damit alle Eigenschaften einer Klasse getestet werden können. Also lautet in diesem Monat die Aufgabe: Implementieren Sie eine Klasse zum automatisierten Testen der INotifyPropertyChanged-Logik. Die zu implementierende Funktionalität ist ein Werkzeug zum Testen von ViewModels. Dieses Werkzeug soll wie folgt bedient werden: NotificationTester.Verify<MyViewModel>(); Die Klasse, die auf INotifyPropertyChanged- Semantik geprüft werden soll, wird als generischer Typparameter an die Methode übergeben. Die Prüfung soll so erfolgen, dass per Reflection alle Eigenschaften der Klasse gesucht werden, die über einen Setter und Getter verfügen. Für diese Eigenschaften soll geprüft werden, ob sie bei einer Zuweisung an die Eigenschaft den PropertyChanged-Event auslösen und dabei den Namen der Eigenschaft korrekt übergeben. Wird der Event nicht korrekt ausgelöst, muss eine Ausnahme ausgelöst werden. Diese führt bei der Ausführung des Tests durch das Unit-Test-Framework zum Scheitern des Tests. Damit man weiß, für welche Eigenschaft die Logik nicht korrekt implementiert ist, sollte die Ausnahme mit den notwendigen Informationen ausgestattet werden, also dem Namen der Klasse und der Eigenschaft, für die der Test fehlschlug. In einer weiteren Ausbaustufe könnte das Werkzeug dann auch auf Klassen angewandt werden, die ebenfalls per Reflection ermittelt wurden. Fasst man beispielsweise sämtliche ViewModels in einem bestimmten Namespace zusammen, kann eine Assembly nach ViewModels durchsucht werden. Damit die so gefundenen Klassen überprüft werden können, muss es möglich sein, das Testwerkzeug auch mit einem Typ als Parameter aufzurufen: NotificationTester.Verify (typeof(myviewmodel)); Im nächsten Heft finden Sie eine Lösung des Problems. Aber versuchen Sie sich zunächst selbst an der Aufgabe. [ml] Listing 3 Property changed? Wer übt, gewinnt In jeder dotnetpro finden Sie eine Übungsaufgabe von Stefan Lieser, die in maximal drei Stunden zu lösen sein sollte. Wer die Zeit investiert, gewinnt in jedem Fall wenn auch keine materiellen Dinge, so doch Erfahrung und Wissen. Es gilt: Falsche Lösungen gibt es nicht. Es gibt möglicherweise elegantere, kürzere oder schnellere Lösungen, aber keine falschen. Wichtig ist, dass Sie reflektieren, was Sie gemacht haben. Das können Sie, indem Sie Ihre Lösung mit der vergleichen, die Sie eine Ausgabe später in dotnetpro finden. Übung macht den Meister. Also los geht s. Aber Sie wollten doch nicht etwa sofort Visual Studio starten public void Name_Property_loest_PropertyChanged_Event_korrekt_aus() { var kunde = new Kunde(); var count = 0; kunde.propertychanged += (o, e) => { count++; Assert.That(e.PropertyName, Is.EqualTo("Name")); ; kunde.name = "Stefan"; Assert.That(count,Is.EqualTo(1)); dotnetpro.dojos

9 INotifyPropertyChanged-Logik automatisiert testen Kettenreaktion Das automatisierte Testen der INotifyPropertyChanged-Logik ist nicht schwer. Man nehme einen Test, verallgemeinere ihn, streue eine Prise Reflection darüber, fertig. Doch wie zerlegt man die Aufgabenstellung so in Funktionseinheiten, dass diese jeweils genau eine definierte Verantwortlichkeit haben? Die Antwort: Suche den Flow! Wie man die INotifyPropertyChanged-Logik automa - tisiert testen kann, habe ich in der Aufgabenstellung zu dieser Übung bereits gezeigt [1]. Doch wie verallgemeinert man nun diesen Test so, dass er für alle Eigenschaften einer Klasse automatisiert ausgeführt wird? Im Kern basiert die Lösung auf folgender Idee: Suche per Reflection alle Properties einer Klasse und führe den Test für die gefundenen Properties aus. Klingt einfach, ist es auch. Aber halt: Bitte greifen Sie nicht sofort zur Konsole! Auch bei vermeintlich unkomplizierten Aufgabenstellungen lohnt es sich, das Problem so zu zerlegen, dass kleine, überschaubare Funktionseinheiten mit einer klar abgegrenzten Verantwortlichkeit entstehen. Suche den Flow! Ich möchte versuchen, die Aufgabenstellung mit einem Flow zu lösen. Doch dazu sollte ich ein klein wenig ausholen und zunächst erläutern, was ein Flow ist und wo seine Vorteile liegen. Vereinfacht gesagt ist ein Flow eine An - ein anderreihung von Funktionen. Ein Argument geht in die erste Funktion hinein, diese berechnet damit etwas und liefert ein Ergebnis zurück. Dieses Ergebnis geht in die nächste Funktion, auch diese berechnet damit wieder etwas und liefert ihr Ergebnis an die nächste Funktion. Auf diesem Weg wird ein Eingangswert nach und nach zu einem Ergebnis transformiert, siehe Listing 1. Die einzelnen Funktionen innerhalb eines Flows, die sogenannten Flowstages, Listing 1 Ein einfacher Flow. var input = "input"; var x1 = A(input); var x2 = B(x1); var result = C(x2); sind zustandslos, das heißt, sie erledigen ihre Aufgabe ausschließlich mit den Daten aus ihren Argumenten. Das hat den Vorteil, dass mehrere Flows asynchron ausgeführt werden können, ohne dass dabei die Zugriffe auf den Zustand synchronisiert werden müssten. Ferner lassen sich zustandslose Funktionen sehr schön automatisiert testen, weil das Ergebnis eben nur von den Eingangsparametern abhängt. Einer nach dem anderen Ein Detail ist bei der Realisierung von Flows ganz wichtig: Weitergereicht werden sollten nach Möglichkeit jeweils Daten vom Typ IEnumerable<T>. Dadurch besteht nämlich die Möglichkeit, auf diesen Daten mit LINQ zu operieren. Ferner können die einzelnen Flowstages dann beliebig große Datenmengen verarbeiten, da bei Verwendung von IEnumerable<T> nicht alle Daten vollständig im Speicher existieren müssen, sondern Element für Element bereitgestellt werden können. Im Idealfall fließt also zwischen den einzelnen Flow - stages immer nur ein einzelnes Element. Es wird nicht etwa das gesamte Ergebnis der ersten Stage berechnet und dann vollständig weitergeleitet. Im Beispiel von Listing 2 führt die Verwendung von yield return dazu, dass der Compiler einen Enumerator erzeugt. Dieser Enumerator liefert nicht sofort die gesamte Aufzählung, sondern stellt auf Anfrage Wert für Wert bereit. Bei Ausführung der Methode Flow() werden also zunächst nur die einzelnen Aufzählungen und Funktionen miteinander verbunden. Erst wenn das erste Element aus dem Ergebnis entnommen werden soll, beginnen die Enumeratoren, Werte zu liefern. Der Flow kommt also erst dann in Gang, wenn jemand hinten das erste Element herauszieht. Als erste ist die Funktion C an der Reihe. Sie entnimmt aus der ihr übergebenen Aufzählung x2 das erste Element. Dadurch kommt B ins Spiel und entnimmt ihrerseits der Aufzählung x1 den ersten Wert. Dies Osetzt sich fort, bis die Methode Input den ersten Wert liefern muss. Im Flow werden die einzelnen Werte sozusagen von hinten durch den Flow gezogen. Ein Flow bietet in Verbindung mit IEnumerable<T> und yield return die Möglichkeit, unendlich große Datenmengen zu verarbeiten, ohne dass eine einzelne Flowstage die Daten komplett im Speicher halten muss. Lesbarkeit durch Extension Methods Verwendet man bei der Implementierung der Flowstages Extension Methods, kann man die einzelnen Stages syntaktisch hintereinanderschreiben, sodass der Flow im Code deutlich in Erscheinung tritt. Dazu muss lediglich der erste Parameter der Funktion um das Schlüsselwort this ergänzt werden, siehe Listing 3. Natürlich müssen die Parameter und Return-Typen der Flow - stages zueinander passen. Lösungsansatz Der erste Schritt des INotifyProperty - Changed-Testers besteht darin, die zu testenden Properties des Typs zu ermitteln. Anschließend muss er jedem dieser Properties einen Wert zuweisen, um zu prüfen, ob der Event korrekt ausgelöst wird. Zum Zuweisen eines Wertes benötigen Sie zur Laufzeit einen Wert vom Typ der Property. Wenn Sie auf eine string-property stoßen, müssen Sie einen string-wert instanzieren, das ist einfach. Komplizierter wird die Sache, wenn der Typ der Property ein komplexer Typ ist. Denken Sie etwa an eine Liste von Points oder Ähnliches. Richtig knifflig wird es, wenn der Typ der Property ein Interfacetyp ist. Dann ist eine unmittelbare Instanzierung nicht möglich. Das Instanzieren der Werte scheint eine eigenständige Funk - tionseinheit zu sein, denn die Aufgabe ist recht umfangreich. Wenn Sie die Properties und ihren jeweiligen Typ gefunden haben, müssen Sie für jede Property einen Test ausführen. Jeder 10 dotnetpro.dojos

10 Listing 2 Rückgabedaten vom Typ IEnumerable nutzen. public void Flow() { var input = Input(); var x1 = A(input); var x2 = B(x1); var result = C(x2); foreach(var value in result) {... public IEnumerable<string> Input() { yield return "Äpfel"; yield return "Birnen"; yield return "Pflaumen"; public IEnumerable<string> A(IEnumerable<string> input) { foreach (var value in input) { yield return string.format("({0)", value); public IEnumerable<string> B(IEnumerable<string> input) { foreach (var value in input) { yield return string.format("[{0]", value); public IEnumerable<string> C(IEnumerable<string> input) { foreach (var value in input) { yield return string.format("-{0-", value); Listing 3 Die Stages syntaktisch koppeln. public static IEnumerable<string> A(this IEnumerable<string> input) { foreach (var value in input) { yield return string.format("({0)", value);... var result = Input().A().B().C(); An dieser Stelle fragen Sie sich möglicherweise, warum ich die Property-Namen als Strings zurückgebe und nicht etwa eine Liste von PropertyInfo-Objekten. Schließlich stecken in PropertyInfo mehr Informationen, insbesondere der Typ der Property, den ich später ebenfalls benötige. Ich habe mich dagegen entschieden, weil dies das Testen der nächsten Flowstage deutlich erschwert hätte. Denn diese hätte dann auf einer Liste von PropertyInfo-Objekten arbeiten müssen. Und da PropertyInfo-Instanzen nicht einfach mit new hergestellt werden können, wären die Tests recht mühsam geworden. Nachdem die Property-Namen bekannt sind, kann die nächste Flowstage dazu den jeweiligen Typ ermitteln. Die Flowstage erhält also eine Liste von Property-Namen sowie den Typ und liefert eine Aufzählung von Typen. static IEnumerable<Type> FindPropertyTypes( this IEnumerable<string> propertynames, Type type) Im Anschluss muss für jeden Typ ein Objekt instanziert werden. Diese Objekte werden später im Test den Properties zuge - wiesen. Die Flowstage erhält also eine Liste von Typen und liefert für jeden dieser Typen eine Instanz des entsprechenden Typs. static IEnumerable<object> GenerateValues( this IEnumerable<Type> types) Dann wird es spannend: Die Actions müssen erzeugt werden. Dabei lässt es sich leider nicht vermeiden, die Property-Namen aus der ersten Stage nochmals zu verwenden. Die Ergebnisse der ersten Stage fließen also nicht nur in die unmittelbar nächste Stage, sondern zusätzlich auch noch in die Stage, welche die Actions erzeugt. Die Namen der Properties werden benötigt, um mittels Reflection die jeweiligen Setter aufrufen zu können. dieser Tests ist eine Action<object>, die auf einer Instanz der Klasse ausgeführt wird, die zu testen ist. Wenn also die Klasse KundeViewModel überprüft werden soll, wird für jede Property eine Action<Kunde View - Model> erzeugt. Sind die Actions erzeugt, müssen sie nur nach ein ander ausgeführt werden. Dabei soll jede Action eine neue Instanz der zu testenden Klasse erhalten. Andernfalls könnte es zu Seiteneffekten beim Testen der Properties kommen. Funktionseinheiten identifizieren Die erste Aufgabe ist also das Ermitteln der zu testenden Properties. Eingangspara - meter in diese Funktionseinheit ist der Typ, für den die INotifyPropertyChanged-Implementierung überprüft werden soll. Das Ergebnis der Flowstage ist eine Aufzählung der Property-Namen. static IEnumerable<string> FindPropertyNames(this Type type) static IEnumerable<Action<object>> GenerateTestMethods(this IEnumerable<object> values, IEnumerable<string> propertynames, Type type) Der letzte Schritt besteht darin, die gelieferten Actions auszuführen. Dazu muss jeweils eine Instanz der zu testenden Klasse erzeugt und an die Action übergeben werden. Abbildung 2 zeigt den gesamten Flow. Die einzelnen Flowstages sind als Extension Method implementiert. Der Flow selbst wird in der öffentlichen Methode NotificationTester.Verify zusammengesteckt. Testen dotnetpro.dojos

11 [Abb. 2] Die zu testenden Properties ermitteln. Listing 4 Eine einfache Testklasse. Um die Property-Setter später aufrufen zu können, muss jeweils ein Objekt vom Typ der Property erzeugt werden. Diese Aufgabe übernimmt die Funktion GenerateValues. Sie erhält als Argument die Liste der Typen und liefert dazu jeweils eine Instanz. Die Funktion ist derzeit recht einfach gehalten. Die Instanz wird einfach durch Verwendung von Activator.CreateInstance erzeugt. Ledigpublic class ClassWithPublicGettersAndSetters { public string StringProperty { get; set; public int IntProperty { get; set; möchte ich die einzelnen Stages aber isoliert. Denn nur so kann ich die Implementierung Schritt für Schritt vorantreiben und muss nicht gleich einen Integrationstest für den gesamten Flow schreiben. Einige Integrationstests sollten am Ende aber auch nicht fehlen. Diese Vorgehensweise hat einen weiteren Vorteil: Um den NotificationTester testen zu können, müssen Testdaten her. Da er auf Typen arbeitet, müssen also Test - daten in Form von Klassen erstellt werden. Das ist nicht nur aufwendig, sondern wird auch schnell unübersichtlich. Ganz kommt man zwar am Erstellen solcher Testklassen auch nicht vorbei, aber der Aufwand ist doch reduziert. Interna testbar machen Um die einzelnen Flowstages isoliert testen zu können, habe ich ihre Sichtbarkeit auf internal gesetzt. Damit sind die Methoden zunächst nur innerhalb der Assembly, in der sie implementiert sind, sichtbar. Um auch in der Test-Assembly darauf zugreifen zu können, muss diese zusätzliche Sichtbarkeit über das Attribut InternalsVisibleTo hergestellt werden: [assembly:internalsvisibleto( "INotifyTester.Tests")] Das Attribut kann prinzipiell in einer beliebigen Quellcodedatei in der Assembly untergebracht werden. Üblicherweise werden Attribute, die sich auf die Assembly beziehen, in der Datei AssemblyInfo.cs untergebracht. Diese finden Sie im Visual Studio Solution Explorer innerhalb des Ordners Properties. Das Sichtbarmachen der internen Methoden nur zum Zwecke des Testens halte ich auf diese Weise für vertretbar. Unit- Tests sind Whitebox-Tests, das heißt, die Art und Weise der Implementierung ist bekannt. Im Gegensatz dazu stehen Blackbox-Tests, die ganz bewusst keine Annahmen über den inneren Aufbau der zu testenden Funktionseinheiten machen. Durch Verwendung von internal ist die Sichtbarkeit nur so weit erhöht, dass die Methoden in Tests angesprochen werden können. Eine vollständige Offenlegung mit public wäre mir zu viel des Guten. Übrigens halte ich es für keine gute Idee, auf die Interna einer zu testenden Klasse mittels Reflection zuzugreifen. Dabei entziehen sich nämlich die Interna, die über Reflec - tion angesprochen werden, den Refaktorisierungswerkzeugen. Und wie man sieht, ist internal in Verbindung mit dem InternalsVisibleTo-Attribut völlig ausreichend. FindPropertyNames Die Namen der Properties werden durch die Flowstage FindPropertyNames geliefert. Dabei entscheidet diese Funktion bereits, welche Properties geprüft werden sollen. Es werden nur Properties berücksichtigt, die über öffentliche Getter und Setter verfügen. Um diese Funktion testen zu können, müssen Testklassen angelegt werden. Das lässt sich leider nicht vermeiden, da die Funktion auf einem Typ als Argument arbeitet. Bei der testgetriebenen Entwicklung steht der Test vor der Implementierung, also gilt es, Testdaten zu erstellen. Ich habe mich zunächst um das Happy Day Szenario gekümmert, also einen Testfall, der später bei der Verwendung typisch ist, siehe Listing 4. Als Nächstes folgt eine Klasse, deren Properties private sind. Diese sollen in den Tests unberücksichtigt bleiben, ihr Name darf also nicht geliefert werden. Die Implementierung der Funktion ist mit LINQ ganz einfach, siehe Listing 5. Die beiden Where-Klauseln sorgen dafür, dass nur Properties berücksichtigt werden, die sowohl einen Getter als auch einen Setter haben. Durch die Binding Flags werden schon Properties ausgeschlossen, die nicht public sind. Durch die Select- Klausel wird festgelegt, wie die zu liefernden Ergebnisse aufgebaut sein sollen. FindPropertyTypes Die Funktion FindPropertyTypes erhält als Argumente die Liste der Property-Namen, die berücksichtigt werden sollen, sowie den Typ, zu dem die Properties gehören. Dazu liefert sie jeweils den Typ der Properties. Auch diese Tests benötigen wieder Testklassen. Ich habe einfach die schon vorhandenen Testklassen verwendet. Auch hier ist die Implementierung dank LINQ nicht schwierig. GenerateValues 12 dotnetpro.dojos

12 Listing 5 Die zu prüfenden Properties finden. internal static IEnumerable<string> FindPropertyNames(Type type) { return type.getproperties(propertybindingflags).where(propertyinfo => propertyinfo.canread).where(propertyinfo => propertyinfo.canwrite).select(propertyinfo => propertyinfo.name); Listing 6 Listing 7 Flowstages zusammenstecken. public static void Verify(Type type) { var propertynames = type.findpropertynames(); propertynames.findpropertytypes(type).generatevalues().generatetestmethods( propertynames, type).executetestmethods(type); Passende Objekte erzeugen. internal static IEnumerable<object> GenerateValues(this IEnumerable<Type> types) { return types.select(type => CreateInstance(type)); internal static object CreateInstance(Type type) { if (type == typeof(string)) { return ""; return Activator.CreateInstance(type); JetBrains ReSharper bezahlt. Der weist nämlich mit der Warnung Access to modified closure auf das Problem hin. ExecuteTestMethods Der letzte Schritt im Flow ist die Ausführung der erzeugten Testmethoden. Diese Methode ist erst durch eine Refaktorisierung entstanden, daher teste ich sie nicht isoliert, sondern nur im Integrationstest. lich Strings werden gesondert behandelt, da die Klasse über keinen parameterlosen Konstruktor verfügt, siehe Listing 6. Die Methode CreateInstance muss sicher im Laufe der Zeit angepasst werden. Sie ist in der gezeigten Implementierung nicht in der Lage, mit komplexen Typen zurechtzukommen. GenerateTestMethods Nun stehen alle Informationen zur Verfügung, um für jede Property eine Testmethode zu erzeugen. Die Funktion Generate- TestMethods erhält drei Argumente: die Liste der Werte für die Zuweisung, die Liste der Property-Namen, den Typ, auf den sich die Tests beziehen. Das Ergebnis ist eine Liste von Actions. static IEnumerable<Action<object>> GenerateTestMethods(this IEnumerable<object> values, IEnumerable<string> propertynames, Type type) Das Testen dieser Funktion kommt leider auch wieder nicht ohne Testklassen aus, denn der Typ geht ja als Argument in die Funktion ein. Die erzeugten Testmethoden werden im Test aufgerufen, um so zu prüfen, dass sie jeweils einen bestimmten Aspekt der INotifyPropertyChanged- Semantik überprüfen. Hier wird es schon schwierig, die Vorgehensweise zu beschreiben, da es sich um Tests handelt, die testen, dass generierte Testmethoden richtig testen, sozusagen Metatests. Die Implementierung der Funktion hat es ebenfalls in sich. Zunächst müssen zwei Aufzählungen im Gleichschritt durchlaufen werden. Dazu wird der Enumerator einer der beiden Aufzählungen ermittelt. Anschließend wird der andere Enumerator in einer foreach-schleife durchlaufen. Innerhalb der Schleife wird der erste Enumerator dann per Hand mit MoveNext und Current bedient. Ich hätte dies gerne in eine Methode ausgelagert, das ist jedoch durch die Verwendung von yield return nicht möglich. Damit sind wir bei der zweiten Besonderheit der Funktion. Die einzelnen Testmethoden werden jeweils mit yield return zurückgeliefert. Da das Ergebnis der Funktion eine Aufzählung von Actions ist, liefert das yield return jeweils eine Action in Form einer Lambda Expression. Dabei müssen die Werte, die aus den Enumeratoren in der Schleife entnommen werden, in lokalen Variablen abgelegt werden, damit sie als Closure in die Lambda Expression eingehen können. Andernfalls würden am Ende alle Lambda Expressions auf demselben Wert arbeiten, nämlich dem aus dem letzten Schleifendurchlauf. Auch hier macht sich übrigens wieder mal der Einsatz von Und jetzt alle! Nun müssen nur noch alle Flowstages zusammengesteckt werden. Das ist einfach, da die Stages als Extension Methods implementiert sind. Dadurch können sie hintereinandergereiht werden, wie Listing 7 zeigt. Der Flow wird lediglich dadurch etwas unterbrochen, dass die Namen der Properties in zwei Flowstages benötigt werden. Daher werden diese nach Ausführung der ersten Stage in einer Variablen zwischengespeichert, die dann weiter unten wieder in eine andere Stage einfließt. Fazit Die Realisierung dieses Testwerkzeugs ging mir recht leicht von der Hand. Dabei hat der Entwurf des Flows relativ viel Zeit in Anspruch genommen. Die anschließende Implementierung ging dafür rasch. Was mir an der Lösung gut gefällt, ist die Tatsache, dass Erweiterungen leicht vorzunehmen sind, weil es klar abgegrenzte Verantwortlichkeiten gibt. Bedarf für Erweiterungen erwarte ich vor allem beim Erzeugen der Testwerte, also in der Funktion CreateInstance. Diese ist bislang relativ einfach gehalten, kann aber leicht erweitert werden. [ml] [1] Stefan Lieser, Zauberwort, INotifyProperty- Changed-Logik automatisiert testen, dotnetpro 4/2010, S. 107, dotnetpro.dojos

13 AUFGABE Testdaten automatisch generieren Meier, Müller, Schulze Nach wie vor spielt die klassische Forms over Data -Anwendung eine große Rolle. Daten aus einer Datenbank sollen per Formular bearbeitet werden. Wenn diese Applikationen getestet werden, spielen Testdaten eine zentrale Rolle. Möglichst viele sollten es sein und möglichst realistisch geformt noch dazu. Stefan, fällt dir dazu eine Übung ein? dnpcode: A1005dojo Wer übt, gewinnt In jeder dotnetpro finden Sie eine Übungsaufgabe von Stefan Lieser, die in maximal drei Stunden zu lösen sein sollte. Wer die Zeit investiert, gewinnt in jedem Fall wenn auch keine materiellen Dinge, so doch Erfahrung und Wissen. Es gilt: Falsche Lösungen gibt es nicht. Es gibt möglicherweise elegantere, kürzere oder schnellere Lösungen, aber keine falschen. Wichtig ist, dass Sie reflektieren, was Sie gemacht haben. Das können Sie, indem Sie Ihre Lösung mit der vergleichen, die Sie eine Ausgabe später in dotnetpro finden. Übung macht den Meister. Also los geht s. Aber Sie wollten doch nicht etwa sofort Visual Studio starten [Abb. 1] So könnte das GUI für einen Testdatengenerator aussehen. Immer wieder begegnet man der Anforderung, Daten aus einer Datenbank in einem Formular zu visualisieren. Oft sind die Datenmengen dabei so groß, dass man nicht einfach alle Daten in einem Rutsch laden sollte. Stattdessen müssen die Daten seitenweise abgerufen und visualisiert werden. Suchen und Filtern kommen meistens hinzu, und schon stellt sich die Frage, ob der gewählte Ansatz auch noch funktioniert, wenn mehr als nur eine Handvoll Testdaten in der Datenbank liegen. Solche Tests auf Echtdaten Ihrer Kunden vorzunehmen wäre übrigens keine gute Idee. Diese unterliegen dem Datenschutz und sollten keinesfalls zu Testzwecken verwendet werden. Und für eine völlig neue Anwendung stehen natürlich noch gar keine Echtdaten zurverfügung. Folglich bleibt nur die Möglichkeit, Testdaten zu generieren. Und genau darum geht es in dieser Übung: Erstellen Sie eine Bibliothek zum Erzeugen von Testdaten. Verschiedene Arten von Testdaten Die generierten Testdaten sollen eine Tabellenstruktur haben. Für jede Spalte wird definiert, von welchem Typ die Werte sind und wie sie erzeugt werden. Anschließend gibt man an, wie viele Zeilen generiert werden sollen, und die Testdaten werden generiert. Die Anforderungen an die Daten können sehr vielfältig sein. Um hier ausreichend flexibel zu sein, sollen die Daten nach verschiedenen Strategien erzeugt werden können. Reine Zufallsdaten sind ein erster Schritt, dürften aber in vielen Fällen nicht ausreichen. Zumindest eine Beschränkung innerhalb vorgegebener Minimum- und Maximumwerte erscheint sinnvoll. Eine weitere Strategie könnte darin bestehen, eine Liste von möglichen Werten vorzugeben, aus denen dann zufällig ausgewählt wird. So könnten beispielsweise Straßennamen generiert werden, die in den Formularen dann auch wie Straßennamen aussehen statt wie zufällig zusammengewürfelte Zeichenfolgen. Es müssen lediglich einige Straßennamen vorgegeben werden. Das Gleiche bietet sich für die Namen von Personen an. Auch hier kann gut mit einer Liste von Namen gearbeitet werden, aus der dann zufällig Werte ausgewählt werden. Die Strategie für die Testdatenerzeugung soll möglichst flexibel sein. Ein Entwickler sollte mit wenig Aufwand einen eigenen Generator ergänzen können. Endergebnis der Datenerzeugung soll eine Aufzählung von Zeilen sein: IEnumerable<object[]> Die generierten Zeilen können dann beliebig verwendet werden. Sie können direkt in Tests einfließen oder auch zuerst als Datei gespeichert werden. Hier bietet sich beispielsweise die Speicherung als CSV-Datei an. Auch das Speichern in einer Datenbank ist natürlich ein typisches Szenario. Das konkrete Speichern der Daten sollte unabhängig sein vom Erzeugen. Es lohnt sich also wieder, sich vor der Implementierung ein paar Gedanken zur Architektur zu machen. Auch bei dieser Übung geht es wieder primär um eine Bibliothek und weniger um eine Benutzerschnittstelle. Wer mag, kann sich aber auch um eine Benutzerschnittstelle kümmern, denn die dürfte hier etwas anspruchsvoller sein. Schließlich benötigen die verschiedenen Generatoren unterschiedliche Eingabedaten. Genügen bei einem Zufallsgenerator vielleicht Minimum und Maximum, müssen bei einem anderen Generator Wertelisten eingegeben werden. Hinzu kommt, dass die Eingabedaten von unterschiedlichem Typ sein können, wofür unterschiedliche Eingabevalidierungen nötig sind. Abbildung 1 zeigt eine erste Skizze einer Benutzerschnittstelle. Und denken Sie stets an die Musiker: Die verbringen die meiste Zeit mit Üben, nicht mit Auftritten! Wir Softwareentwickler sollten auch regelmäßig üben, statt immer nur zu performen. Schließlich sollte man beim Auftritt keine Fehler machen, nur beim Üben ist das zulässig und sogar erwünscht: ohne Fehler keineweiterentwicklung. Also üben Sie und machen Sie Fehler! [ml] 14 dotnetpro.dojos

14 Testdaten automatisch generieren Tückisches GUI Bei dieser Übung ging der Kern der Anwendung relativ leicht von der Hand. Die eigentliche Herausforderung lag in der dynamischen Benutzerschnittstelle. Jeder Datentyp verlangt andere Oberflächenelemente. Und der Anwender will seine Daten individuell strukturieren können. Komponente Eine Komponente ist eine binäre Funktionseinheit mit separatem Kontrakt: Binär bedeutet hier, dass die Komponente an den Verwendungsstellen binär referenziert wird. Es wird also bei der Verwendung keine Referenz auf das entsprechende Visual-Studio-Projekt gesetzt, sondern eine Referenz auf die erzeugte Assembly. Separater Kontrakt bedeutet, dass das Interface für die Komponente in einer eigenen Assembly abgelegt ist und nicht in der Assembly liegt, in welcher die Komponente implementiert ist. Daraus folgt, dass eine Komponente immer aus mindestens zwei Assemblies besteht, nämlich einer für den Kontrakt und einer für die Implementierung. Und natürlich gehören Tests dazu also besteht jede Komponente aus mindestens drei Projekten. [Abb. 1] System- Umwelt- Diagramm, Version 1. [Abb. 2] System-Umwelt-Diagramm, Version 2. A uch bei dieser Aufgabe zeigte sich wieder, wie wichtig es ist, sich vor der Implementierung ein paar Gedanken zur Architektur zu machen. Der erste Gedanke, das Erzeugen der Daten vom Speichern zu trennen, liegt auf der Hand und wurde in der Aufgabenstellung schon erwähnt. Doch wie geht man generell vor, wenn für eine Aufgabenstellung eine Architektur entworfen werden soll? Ganz einfach: Man malt den kleinen König. Den gibt es immer, denn er ist schließlich derjenige, der die Anforderungen formuliert hat. Er ist der Grund dafür, dass das System überhaupt gebaut wird. Das zu implementierende System als Ganzes kann man auch sofort hinmalen. Damit liegt man nie verkehrt. Es ergibt sich damit das in Abbildung 1 gezeigte Bild. Das Diagramm nennt sich System-Umwelt-Diagramm, da es das System in seiner Umwelt zeigt. In der Umwelt des Systems gibt es immer mindestens einen Client, den kleinen König, der das System bedient. Bei manchen Systemen mag es mehrere unterschiedliche Clients geben, das spielt für den Testdatengenerator jedoch keine Rolle. Die zweite Kategorie von Elementen in der Umwelt stellen Ressourcen dar. Diese liegen außerhalb des zu erstellenden Systems und sollten daher in das System-Umwelt-Diagramm aufgenommen werden, denn unser System ist von diesen Ressourcen abhängig. Im Fall des Testdatengenerators sind als Ressourcen in der Umwelt CSV-Dateien und Datenbanken denkbar. Irgendwo müssen die generierten Testdaten schließlich hin. Folglich ergänze ich das System-Umwelt-Diagramm um diese Ressourcen. Das Ergebnis ist in Abbildung 2 zu sehen. Wer nun glaubt, ein solches Diagramm sei ein Taschenspielertrick, um Zeit zu schinden, ohne Nutzen für den Architekturentwurf, der irrt. Denn aus diesem Bild wird bereits deutlich, welche Komponenten mindestens entstehen müssen. Den Begriff Komponente verwende ich hier mit einer festen Bedeutung, siehe dazu die Erläuterungen im Kasten. Der Kern des Systems sollte gegenüber der Umwelt abgeschirmt werden, weil das System die Umwelt nicht kontrollieren kann. Die Umwelt kann sich verändern. Es können etwa neue Clients hinzukommen oder auch zusätzliche Ressourcen. Folglich müssen auf der Umrandung des Systems Komponenten entstehen, die den Kern des Systems über definierte Schnittstellen gegenüber der Umwelt isolieren. Andernfalls würde der Kern des Systems immer wieder von Änderungen in der Umwelt betroffen sein und wäre damit sehr anfällig. Und darin liegt die Bedeutung des System-Umwelt- Diagramms: Es zeigt, welche Komponenten das System von der Umwelt abschirmen. Für Clients, die das System verwenden, bezeichnen wir die Komponente, über welche der Client mit dem System interagiert, als Portal. In Abhängigkeitsdiagrammen werden Portale immer als Quadrate dargestellt. Die Interaktion des Systems mit Ressourcen erfolgt über Adapter. Diese werden durch Dreiecke symbolisiert. Im konkreten Fall des Testdatengenerators können wir aufgrund des System-Umwelt-Diagramms also schon vier Komponenten identifizieren, siehe Abbildung 3: Portal, CSV-Adapter, Datenbank-Adapter, Testdatengenerator. Die Komponenten sollten normalerweise allerdings nicht im System-Umwelt-Diagramm eingezeichnet werden, weil dort sonst zwei Belange vermischt werden. Es soll hier nur gezeigt werden, dass sich Portal und Adapter immer sofort aus dem System-Umwelt-Diagramm ergeben. Aus dem in Abbildung 3 gezeigten Diagramm lässt sich das in Abbildung 4 gezeigte Abhängigkeitsdiagramm ableiten. Den Kern zerlegen Nachdem ich diese Komponenten identifiziert hatte, habe ich die Aufgabenstellung dotnetpro.dojos

15 [Abb. 3] System-Umwelt- Komponenten. [Abb. 4] Abhängigkeitsdiagramm. [Abb. 6] Abhängigkeitsdiagramm der Komponenten. [Abb. 5] Generatoren, nach Typ geordnet, in Unterverzeichnissen. Erzeugen der Daten zerlegt. Aufgabe des Testdatengenerators ist es, Datenzeilen zu erzeugen. Dabei soll jede Datenzeile aus mehreren Spalten bestehen. Diese Aufgabe kann in folgende Funktionseinheiten zerlegt werden: Erzeugen eines einzelnen Wertes, Erzeugen einer Zeile, Erzeugen mehrerer Zeilen. Dabei scheint die Trennung in das Erzeugen einer Zeile und das Erzeugen mehrerer Zeilen auf den ersten Blick möglicherweise etwas merkwürdig. Wenn eine Zeile erzeugt werden kann, genügt doch eine simple Schleife, und schon können mehrere Zeilen erzeugt werden. Dennoch halte ich es für wichtig, diese beiden Funktionseinheiten zu identifizieren. Denn für die testgetriebene Entwicklung ist es nützlich, im Vorfeld zu wissen, welche Funktionseinheiten auf einen zukommen. So fällt es nämlich viel leichter, ausreichende Testfälle zu finden, sprich: die Anforderungen zu klären. Und bei den Anforderungen liegt die Herausforderung eher darin, klar zu definieren, was die Anforderungen an das Erzeugen einer einzelnen Zeile sind. Dies dann zu übertragen auf die Erzeugung mehrerer Zeilen ist in der Tat trivial. Aber ohne die Trennung würde möglicherweise nur eine Funktionseinheit entstehen, die mehrere Datenzeilen erzeugt. Das würde die testgetriebene Entwicklung unnötig erschweren. Nachdem ich für das Erzeugen der Daten die Funktionseinheiten identifiziert hatte, habe ich überlegt, welche davon Komponenten werden sollen. Erst Komponenten erlauben eine parallele Entwicklung von Funktionseinheiten durch mehrere Entwickler oderteams gleichzeitig. Dies ist zwar hier nicht das Ziel, doch resultiert aus der Trennung von Kontrakt und Implementierung, dass die Komponenten austauschbar sind. Dies betrachte ich beim Testdatengenerator an einer Stelle für besonders wichtig: bei den Generatoren. Die werden später sicher immer wieder ergänzt werden. Da ist es hilfreich, wenn dann nicht jeweils die gesamte Anwendung neu übersetzt werden muss, sondern neue Generatoren mit geringem Aufwand ergänzt werden können. In einer weiteren Ausbaustufe wäre es sogar denkbar, die Generatoren zur Laufzeit zu laden. Dann könnten später beliebige zusätzliche Generatoren verwendet werden, ohne dass am Testdatengenerator selbst etwas geändert werden muss. Damit sind die Generatoren zunächst einmal eine Komponente. Eine andere Aufteilung wäre ebenfalls denkbar, man könnte Generatoren zum Beispiel nachtyp in Komponenten zusammenfassen. Eine Komponente mit Stringgeneratoren, eine für int- Generatoren et cetera. Zurzeit sind es nur wenige Generatoren, daher habe ich mich dafür entschieden, sie alle in einer Komponente unterzubringen. Innerhalb der Komponente habe ich die Generatoren nach Typ in Unterverzeichnisse geordnet. Dies ist in Abbildung 5 zu sehen. Eine weitere Komponente bildet die Funktionseinheit, die dafür zuständig ist, Zeilen aus Einzelwerten zu bilden. Diese Komponente habe ich DataPump genannt. Eine dritte Komponente bildet das Speichern der Daten. Implementiert habe ich einen CsvDataAdapter. Ein DbDataAdapter zum Speichern der Testdaten in einer 16 dotnetpro.dojos

16 Listing 1 Einen generischen Typparameter verwenden. public interface IGenerator<T> { T GenerateValue(); Datenbank liegt auf der Hand, auf diesen habe ich aus Zeitgründen jedoch verzichtet. Übergangsweise kann man sich damit behelfen, die CSV-Dateien mit einem ETL- Prozess (Extract, Transform, Load) in die Datenbank zu schaufeln. Die Komponenten Generators, Data- Pump, DbDataAdapter und CsvDataAdapter haben nur geringe Abhängigkeiten, wie Abbildung 6 zeigt. Der CsvDataAdapter ist nicht von den anderen Komponenten abhängig, weil er lediglich auf dem gemeinsamen Datenmodell aufsetzt. Einzelne Werte Listing 2 Spalten definieren. public class ColumnDefinition<T> { public ColumnDefinition(string columnname, IGenerator<T> generator) { ColumnName = columnname; Generator = generator; public string ColumnName { get; private set; public IGenerator<T> Generator { get; private set; Listing 3 Werte erzeugen. public static IEnumerable<object> GenerateValues(this IEnumerable<ColumnDefinition> columndefinitions) { return columndefinitions.select(x => x.generator).select(x => x.generatevalue()); Listing 4 Eine Datenzeile generieren. public static Line GenerateLine(this IEnumerable<object> values) { return new Line(values); Listing 5 Mehrere Zeilen generieren. public IEnumerable<Line> GenerateTestData(IEnumerable<ColumnDefinition> columndefinitions, int rowcount) { for (var i = 0; i < rowcount; i++) { yield return columndefinitions.generatevalues().generateline(); Für das Erzeugen eines einzelnen Wertes habe ich mich für die Verwendung eines Generators entschieden. Dieser hat die Aufgabe, zu einem gegebenen Typ einen Wert zu liefern. Die dabei verwendete Strategie bestimmt der Generator. So ist ein Generator denkbar, der zufällige Werte erzeugt. Genauso kann aber auch ein Generator erstellt werden, der eine Liste von Daten erhält und daraus zufällig auswählt. Die Beschreibung der zu erzeugenden Datenzeilen besteht also darin, pro Spalte einen Generator zu definieren. Ferner wird pro Spalte der Name der Spalte benötigt. Im Kontrakt der Generatoren habe ich einen generischen Typparameter verwendet, siehe Listing 1. Dadurch wird bereits zur Übersetzungszeit geprüft, ob der Rückgabewert der Methode GenerateValue zum Generatortyp passt. Die Generatoren werden in den Spaltendefinitionen verwendet. Da sie einen generischen Typparameter haben, muss dieser bei Verwendung des Generators entweder durch einen konkreten Typ oder an derverwendungsstelle durch einen generischen Typparameter belegt werden. Für die Klasse ColumnDefinition würde das bedeuten, dass diese ebenfalls einen generischen Typparameter erhält, siehe Listing 2. So weit, so gut. Doch eine Zeile besteht aus mehreren Spalten. Daher müssen mehrere ColumnDefinition<T>-Objekte in einer Liste zusammengefasst werden. Da natürlich jede Spalte einen anderen Typ haben kann, muss es möglich sein, beispielsweise eine ColumnDefinition<string> sowie eine ColumnDefinition<int> in diese Liste aufzunehmen. Dies ist jedoch mit C# 3.0 aufgrund der fehlenden Ko-/Kontravarianz noch nicht möglich. Würde man die Liste als List<object> definieren, müsste die Liste Kovarianz unterstützen. Das tut sie jedoch nicht, Ko- und Kontravarianz stehen erst dotnetpro.dojos

17 mit C# 4.0 zur Verfügung. Ich habe daher den Generator in der ColumnDefinition als IGenerator<object> definiert, statt Column- Definition generisch zu machen. Dies kann man dann mit Erscheinen von Visual Studio 2010 ändern. Eine Zeile Durch die Generatoren können die einzelnen Werte der Spalten erzeugt werden. Um eine ganze Datenzeile zu erzeugen, muss jeder Generator einmal aufgerufen werden, um seinen jeweils nächsten Wert zu liefern. Dies ist bei Verwendung von LINQ ganz einfach, siehe Listing 3. In der Methode wird über die Aufzählung der Spaltendefinitionen iteriert und durch das erste Select jeweils der Generator aus der Spaltendefinition entnommen. Durch das zweite Select wird aus jedem Generator ein Wert abgerufen. Das Ergebnis ist eine Aufzählung der von den Generatoren gelieferten Werte. Diese Aufzählung wird später an den Konstruktor einer Zeile übergeben, siehe Listing 4. Mehrere Zeilen Die Erzeugung mehrerer Zeilen erfolgt in einer for-schleife. Dabei wird die Schleife so oft durchlaufen, dass die Anzahl der gewünschten Datensätze erzeugt wird. Dabei kommt wieder einmal ein yield return zum Einsatz, siehe Listing 5. Flow Und schon wieder konnte ich einen Flow identifizieren. Die Aufzählung der Column- Definitions fließt in die Methode Generate- Values. Heraus kommt eine Aufzählung mit Werten. Diese wird weitergeleitet in die Methode GenerateLine, die aus den Werten eine Zeile erstellt: columndefinitions.generatevalues().generateline(); Um den Flow so formulieren zu können, sind die beiden Methoden als Extension Methods realisiert. Dadurch wird das Aneinanderreihen der Methoden besonders einfach. Abbildung 7 zeigt den Flow. Damit ist die Komponente DataPump bereits beschrieben. Weiter geht es bei den Generatoren. Generatoren Ein Generator ist für das Erzeugen von Werten eines bestimmten Typs zuständig. Welche Strategie dabei verfolgt wird, ist Sache des Generators. Dies soll am Beispiel [Abb. 7] Flow zum Erzeugen einer Datenzeile. eines Generators für int-werte gezeigt werden, der zufällige Werte innerhalb vorgegebener Minimum- und Maximumwerte erzeugt. Die Implementierung des Generators ist ganz einfach. Ich verwende einen Zufallszahlengenerator System.Random aus dem.net Framework und weise ihn an, einen Listing 6 Ein Generator für int-werte. public class RandomIntGenerator : IGenerator<object> { private readonly int minimum; private readonly int maximum; private readonly Random random;... Listing 7 Den Zufallsgenerator testen. [TestFixture] public class RandomIntGeneratorTests { private RandomIntGenerator sut;... public object GenerateValue() { return random.next(minimum, maximum + 1); [SetUp] public void Setup() { sut = new RandomIntGenerator(1, 5, new Random(0)); Wert innerhalb der definierten Grenzen zu liefern, siehe Listing 6. Die spannende Frage ist nun: Wie kann man einen solchen Generator testen, der zufällige Werte liefern soll? Sie werden bemerkt haben, dass oben im Listing der Zufallszahlengenerator random nirgendwo instanziert und zugewiesen wird. Dies liegt in der Notwendig- public void Zufaellige_Werte_zwischen_Minimum_und_Maximum_werden_geliefert() { Assert.That(sut.GenerateValue(), Is.EqualTo(4)); Assert.That(sut.GenerateValue(), Is.EqualTo(5)); 18 dotnetpro.dojos

18 keit begründet, den Generator automatisiert testen zu können. Würde der Generator den Zufallszahlengenerator selbst instanzieren, würde er immer zufällige Werte liefern. Dies soll er natürlich tun, aber im Test benötigen wir die Kontrolle darüber, welche Werte zufällig geliefert werden, siehe Listing 7. Die Testmethode ist hier verkürzt dargestellt. Ich rufe im Test so lange Werte ab, bis alle möglichen Werte innerhalb von Minimum und Maximum mindestens einmal geliefert wurden. Der Trick, dass sich der Random-Generator immer gleich verhält, liegt darin, dass ich ihn im Test immer mit demselben Startwert (Seed) 0 instanziere. Um das zu ermöglichen, habe ich einen internal-konstruktor ergänzt, der nur im Test verwendet wird, um den Random-Generator in die Klasse zu injizieren. Der öffentliche Konstruktor der Klasse instanziert den Random-Generator ohne Seed, sodass dieser zufällige Werte liefert, siehe Listing 8. Bei Konstruktoren sollte man übrigens generell das Highlander-Prinzip beachten: Es kann nur einen geben (eine Anspielung auf den Film Highlander Es kann nur einen geben). Der interne Konstruktor ist derjenige, der die eigentliche Arbeit verrichtet. Der öffentliche Konstruktor verfügt nur über die beiden Parameter für Minimum und Maximum. Er bezieht sich auf den internen Konstruktor und übergibt diesem, neben den beiden Grenzwerten, auch einen mit new Random() erzeugten Random-Generator. Das Highlander-Prinzip sollte beachtet werden, damit es in den Konstruktoren nicht zur Verletzung des Prinzips Don t Repeat Yourself (DRY) kommt. Der öffentliche Konstruktor könnte ja die Grenzwerte selbst an die Felder zuweisen, dann würden diese Zuweisungen jedoch an zwei Stellen auftreten. Eine weitere interessante Implementierung bietet der RollingIntGenerator. Er liefert, ausgehend von einem Minimumwert, [Abb. 8] Das fertige Portal. Listing 8 Zufallsgenerator für int-werte. public RandomIntGenerator(int minimum, int maximum) : this(minimum, maximum, new Random()) { internal RandomIntGenerator(int minimum, int maximum, Random random) { this.minimum = minimum; this.maximum = maximum; this.random = random; Listing 9 Zufällig einen Stringwert auswählen. internal RandomSelectedStringsGenerator(Random random, params string[] values) { this.random = random; this.values = values; immer den nächsten Wert, bis er beim Maximumwert angekommen ist. Dann wird wieder von vorn begonnen. Bei diesem Generator lag die Herausforderung darin, korrekt mit dem größtmöglichen int-wert (int.maxvalue) umzugehen. Ohne Unit- Tests wäre das ein elendiges Rumprobieren geworden. So war es ganz leicht. Für Stringwerte habe ich einen Generator implementiert, der eine Liste von Strings erhält und daraus zufällig einen auswählt. Die zur Verfügung stehenden Strings habe ich im Konstruktor als Parameter-Array definiert, siehe Listing 9. Das ist für die Unit-Tests ganz angenehm, weil man einfach eine beliebige Liste von Stringwerten übergeben kann: sut = new RandomSelectedStringsGenerator( new Random(0), "Apfel", "Birne", "Pflaume"); Bei der Verwendung des Generators aus Sicht einer Benutzerschnittstelle ist es wünschenswert, einen String zu übergeben, der eine Liste von Werten enthält, die mit Semikolon getrennt sind: "Apfel; Birne; Pflaume" Um das zu ermöglichen, habe ich eine separate Extension Method ToValues() implementiert, die einen String entsprechend zerlegt. Diese Methode kann bei Bedarf in den Konstruktoraufruf eingesetzt werden: "Apfel; Birne; Pflaume".ToValues().ToArray() Natürlich hätte ich das Zerlegen des Strings in die Einzelwerte auch im entsprechenden Generator implementieren können. Dann hätte der sich aber um mehr als eineverantwortlichkeit gekümmert. Ferner war die Implementierung so etwas einfacher, da ich mich jeweils auf eine einzelne Aufgabenstellung konzentrieren konnte. Portal Das Portal hatte es in sich. Obwohl ich mit WPF schon einiges gemacht habe, fühlte ich mich etwas unsicher, diese sehr dynamische Aufgabenstellung mitwpf anzugehen, und entschied mich daher, das Problem mit Windows Forms zu lösen, weil mir das schneller von der Hand geht. Doch der Reihe nach. Wie die Benutzerschnittstelle des Testdatengenerators ungefähr aussehen könnte, habe ich in der Aufgabenstellung bereits durch ein Mockup angedeutet. Abbildung 8 zeigt, wie mein Ergebnis aussieht. dotnetpro.dojos

19 Listing 10 Eine Spalte definieren. public class SpaltenDefinition { public string Bezeichnung { get; set; public Type ControlType { get; set; public Func<string, object, ColumnDefinition> Columndefinition { get; set; Listing 11 Spalten definieren. new SpaltenDefinition { Bezeichnung = "Random DateTime", ControlType = typeof(minimummaximum), Columndefinition = (columnname, control) => new ColumnDefinition(columnName, new RandomDateTimeGenerator( DateTime.Parse(((MinimumMaximum)control).Minimum), DateTime.Parse(((MinimumMaximum)control).Maximum))) Ich sehe beim Portal zwei Herausforderungen: Die Anzahl der Spalten in den zu generierenden Daten ist variabel. Daraus ergibt sich, dass die Anzahl der Controls für Spaltendefinitionen variabel sein muss. Im Mockup habe ich daher Schaltflächen vorgesehen, mit denen eine Spaltendefinition entfernt bzw. hinzugefügt werden kann. Die zweite Herausforderung sehe ich im Aufbau der Spaltendefinitionen. Je nach ausgewähltem Generatortyp sind unterschiedliche Eingaben notwendig. Mal sind zwei Textfelder für Minimum und Maximum erforderlich, mal nur eine für die Elemente einer Liste. Das heißt, dass sich der Aufbau der Benutzeroberfläche mit der Wahl des Generatortyps ändert. Um diese beiden Herausforderungen möglichst isoliert angehen zu können, habe ich für die variablen Anteile einer Spaltendefinition mit UserControls gearbeitet. So habe ich für einen Generator, der Minimum- und Maximumwerte benötigt, ein UserControl erstellt, in dem zwei Textboxen mit zugehörigen Labels zusammengefasst sind. Wird aus der Dropdownliste ein Generator ausgewählt, muss das zum Generator passende Control angezeigt werden. Ferner muss zum ausgewählten Generator später die zugehörige ColumnDefinition erzeugt werden, um damit dann die Daten zu generieren. Diese Informationen habe ich im Portal in einer Datenklasse Spalten- Definition zusammengefasst. Objekte dieser Klasse werden direkt in der Dropdownliste verwendet. Daher enthält die Spalten- Definition auch eine Beschreibung. Diese wird als DisplayMember in der Dropdownliste angezeigt, siehe Listing 10. Die Eigenschaft ControlType enthält den Typ des zu verwendenden Controls. Genügt ein Textfeld, kann hier typeof(textbox) gesetzt werden. In komplizierteren Fällen wird der Typ eines dafür implementierten UserControls gesetzt. Um für den ausgewählten Generatortyp eine ColumnDefinition erzeugen zu können, habe ich eine Eigenschaft ergänzt, die eine Funktion erhält, die genau dies bewerkstelligt: Sie erzeugt eine ColumnDefinition. Dazu erhält sie als Eingangsparameter zum einen den Namen der Spalte, zum anderen das Control mit allen weiteren Angaben. Da der Typ des Controls variabel ist, wird es vom Typ object übergeben. Die Funktion muss dieses Objekt dann auf den erwarteten Typ casten. Bei der Initialisierung des Portals wird für die verfügbaren Generatoren jeweils eine Spaltendefinition erzeugt und in die Item-Liste des Dropdown-Controls gestellt, siehe Listing 11. Interessant hierbei ist die Lambda Expression. Diese erhält die beiden Parameter columnname und control und erzeugt daraus eine ColumnDefinition mit dem ausgewählten Generator. Da diese Lambda [Abb. 9] Document Outline. Expression im Kontext einer SpaltenDefinition steht, kann das übergebene Control gefahrlos auf den Typ gecastet werden, der auch in der Eigenschaft ControlType verwendet wird. Auch hier sähe eine Lösung mit Generics sicher eleganter aus, ist aber ohne Ko-/Kontravarianz nicht möglich. Wird nun in der Combobox ein anderer Generatortyp ausgewählt, muss das in der Spaltendefinition angegebene Control angezeigt werden. Um dynamisch die zugehörigen Controls zu finden, füge ich alle Controls, die zu einer Spalte gehören (Plusund Minus-Button, Textfeld für den Spaltennamen, Combobox, Platzhalter für UserControl) in ein Panel ein. Um in diesem Panel später dynamisch das UserControl austauschen zu können, füge ich dieses zusätzlich in ein weiteres Panel. Dieses dient jeweils als Platzhalter für das auszutauschende Control. In der Form sind nur wenige statische Elemente vorhanden. Den Aufbau der Form zeigt die Document Outline in Abbildung 9. Darin ist dargestellt, wie die einzelnen Controls ineinandergeschachtelt sind. Abbildung 10 zeigt, wie die Controls für eine Spaltendefinition dynamisch zur Laufzeit zusammengesetzt werden. Dabei zeigen die Pfeile an, auf welches Control gegebenenfalls die Tag-Eigenschaft verweist. Nun zur zweiten Herausforderung, dem dynamischen Ergänzen und Löschen von Spaltendefinitionen. Jede Spaltendefinition verfügt über die beiden Schaltflächen zum Hinzufügen und Löschen von Spaltendefinitionen. Zurzeit füge ich eine neue Spaltendefinition jeweils ans Ende an, künftig könnte diese aber auch an der betreffenden Position eingefügt werden. Daher habe ich bereits an jeder Spaltendefinition einen Plus-Button vorgesehen. Für die Aufnahme aller Spaltenbeschreibungen ist im statischen Teil der Form ein Panel zuständig. Wird mit der Minus-Schaltfläche versucht, eine Spaltenbeschreibung zu entfernen, müssen die zugehörigen Controls aus die- 20 dotnetpro.dojos

20 [Abb. 10] Controls im Panel. sem Panel entfernt werden. Um dies zu vereinfachen, ist das zugehörige Panel an der Tag-Eigenschaft des Buttons gesetzt. So weiß der Button, zu welchem Panel er gehört und kann dieses aus dem umschließenden Panel entfernen. Wird im Portal die Schaltfläche Generieren angeklickt, muss für jede Spaltenbeschreibung eine ColumnDefinition erzeugt werden, um dann die Testdaten zu generieren. Dazu wird die Liste der Spaltenbeschreibungen im statischen Panel durchlaufen. Darin befindet sich jeweils ein Textfeld, das den Namen der Spalte enthält. Ferner befindet sich im Platzhalterpanel ein Control, in dem die Parameter für den Generator enthalten sind. In der Dropdownliste enthält das SelectedItem eine Spalten- Definition, aus der sich die ColumnDefinition erstellen lässt. Dazu wird aus der SpaltenDefinition die Funktion zum Erzeugen der ColumnDefinition aufgerufen. Insgesamt hat das Erstellen des Portals knapp zwei Stunden in Anspruch genommen. Automatisierte Tests habe ich dazu fast keine erstellt. Diese würde ich allerdings in einem echten Projekt im Nachhinein ergänzen, da die Logik für den dynamischen Aufbau des Portals doch recht umfangreich geworden ist. Um hier bei späteren Erweiterungen Fehler auszuschließen, würde ich die typischen Bedienungsschritte eines Anwenders automatisiert testen. Host Am Ende benötigen wir für die gesamte Anwendung noch eine EXE-Datei, mit der die Anwendung gestartet werden kann. Aufgabe dieses Hosts ist es, die benötigten Komponenten zu beschaffen und sie den Abhängigkeiten gemäß zu verbinden. Die Abhängigkeiten sind hier in Form von Konstruktorparametern modelliert. Folglich muss der Host die Komponenten in der richtigen Reihenfolge instanzieren, im Abhängigkeitsbaum von unten nach oben, von den Blattknoten zur Wurzel. Anschließend übergibt er die Kontrolle an das Portal. Für die vorliegende Anwendung, bestehend aus einer Handvoll Komponenten, ist diese Aufgabe trivial. Bei größeren Anwendungen kostet diese Handarbeit Zeit und sollte automatisiert werden. Die Grundidee dabei ist: Man überlässt das Instanzieren der Komponenten einem DI-Container wie StructureMap [2] oder Castle Windsor [3]. Über ein eigenes Interface identifiziert man den Startpunkt der Anwendung, und los geht s. Ein solcher Host kann dann sogar generisch sein und in allen Anwendungen verwendet werden. Denkbare Erweiterungen Für wiederkehrende Aufgaben wäre es sinnvoll, das Schema der Datengenerierung speichern und laden zu können. Dies kann beispielsweise mit dem Lounge Repository [4] erfolgen. In der Architektur würde dafür ein weiterer Adapter ergänzt, mit dem ein Schema gespeichert und geladen werden kann. Natürlich müssten im Portal entsprechende Anpassungen vorgenommen werden, um entsprechende Menüfunktionen zu ergänzen. Des Weiteren wäre es denkbar, die Generatoren zur Laufzeit dynamisch zu laden. Damit könnten Entwickler ihre eigenen Generatoren implementieren und verwenden, ohne dazu die gesamte Anwendung übersetzen zu müssen. Mithilfe eines DI- Containers wie StructureMap oder des Managed Extensibility Framework MEF [5] sollte auch diese Erweiterung keine große Hürde darstellen. Fazit Bei dieser Aufgabe stellte sich heraus, dass die Benutzerschnittstelle einer Anwendung durchaus einige Zeit in Anspruch nehmen kann. Die eigentliche Funktionalität war dagegen schnell entworfen und implementiert. Das lag maßgeblich daran, dass ich mir im Vorfeld einige Gedanken über die Architektur gemacht hatte. Danach ging die testgetriebene Entwicklung flüssig von der Hand. [ml] [1] Stefan Lieser, Meier, Müller, Schulze, Testdaten automatisch generieren, dotnetpro 5/2010, Seite 108ff., [2] [3] [4] und Ralf Westphal, Verflixte Sucht, dotnetpro 11/2009, Seite 52f. [5] Wir liefern passgenaue Strategien und Lösungen für Ihre Inhalte auf iphone/ipad Android BlackBerry Windows Phone 7 dem mobilen Browser Besuchen Sie uns unter dotnetpro.dojos.2011

M. Graefenhan 2000-12-07. Übungen zu C. Blatt 3. Musterlösung

M. Graefenhan 2000-12-07. Übungen zu C. Blatt 3. Musterlösung M. Graefenhan 2000-12-07 Aufgabe Lösungsweg Übungen zu C Blatt 3 Musterlösung Schreiben Sie ein Programm, das die Häufigkeit von Zeichen in einem eingelesenen String feststellt. Benutzen Sie dazu ein zweidimensionales

Mehr

Objektorientierte Programmierung für Anfänger am Beispiel PHP

Objektorientierte Programmierung für Anfänger am Beispiel PHP Objektorientierte Programmierung für Anfänger am Beispiel PHP Johannes Mittendorfer http://jmittendorfer.hostingsociety.com 19. August 2012 Abstract Dieses Dokument soll die Vorteile der objektorientierten

Mehr

Persönliche Zukunftsplanung mit Menschen, denen nicht zugetraut wird, dass sie für sich selbst sprechen können Von Susanne Göbel und Josef Ströbl

Persönliche Zukunftsplanung mit Menschen, denen nicht zugetraut wird, dass sie für sich selbst sprechen können Von Susanne Göbel und Josef Ströbl Persönliche Zukunftsplanung mit Menschen, denen nicht zugetraut Von Susanne Göbel und Josef Ströbl Die Ideen der Persönlichen Zukunftsplanung stammen aus Nordamerika. Dort werden Zukunftsplanungen schon

Mehr

Mediator 9 - Lernprogramm

Mediator 9 - Lernprogramm Mediator 9 - Lernprogramm Ein Lernprogramm mit Mediator erstellen Mediator 9 bietet viele Möglichkeiten, CBT-Module (Computer Based Training = Computerunterstütztes Lernen) zu erstellen, z. B. Drag & Drop

Mehr

Stellen Sie bitte den Cursor in die Spalte B2 und rufen die Funktion Sverweis auf. Es öffnet sich folgendes Dialogfenster

Stellen Sie bitte den Cursor in die Spalte B2 und rufen die Funktion Sverweis auf. Es öffnet sich folgendes Dialogfenster Es gibt in Excel unter anderem die so genannten Suchfunktionen / Matrixfunktionen Damit können Sie Werte innerhalb eines bestimmten Bereichs suchen. Als Beispiel möchte ich die Funktion Sverweis zeigen.

Mehr

Suche schlecht beschriftete Bilder mit Eigenen Abfragen

Suche schlecht beschriftete Bilder mit Eigenen Abfragen Suche schlecht beschriftete Bilder mit Eigenen Abfragen Ist die Bilderdatenbank über einen längeren Zeitraum in Benutzung, so steigt die Wahrscheinlichkeit für schlecht beschriftete Bilder 1. Insbesondere

Mehr

Professionelle Seminare im Bereich MS-Office

Professionelle Seminare im Bereich MS-Office Der Name BEREICH.VERSCHIEBEN() ist etwas unglücklich gewählt. Man kann mit der Funktion Bereiche zwar verschieben, man kann Bereiche aber auch verkleinern oder vergrößern. Besser wäre es, die Funktion

Mehr

Objektorientierte Programmierung

Objektorientierte Programmierung Objektorientierte Programmierung 1 Geschichte Dahl, Nygaard: Simula 67 (Algol 60 + Objektorientierung) Kay et al.: Smalltalk (erste rein-objektorientierte Sprache) Object Pascal, Objective C, C++ (wiederum

Mehr

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren Lineargleichungssysteme: Additions-/ Subtraktionsverfahren W. Kippels 22. Februar 2014 Inhaltsverzeichnis 1 Einleitung 2 2 Lineargleichungssysteme zweiten Grades 2 3 Lineargleichungssysteme höheren als

Mehr

40-Tage-Wunder- Kurs. Umarme, was Du nicht ändern kannst.

40-Tage-Wunder- Kurs. Umarme, was Du nicht ändern kannst. 40-Tage-Wunder- Kurs Umarme, was Du nicht ändern kannst. Das sagt Wikipedia: Als Wunder (griechisch thauma) gilt umgangssprachlich ein Ereignis, dessen Zustandekommen man sich nicht erklären kann, so dass

Mehr

1 topologisches Sortieren

1 topologisches Sortieren Wolfgang Hönig / Andreas Ecke WS 09/0 topologisches Sortieren. Überblick. Solange noch Knoten vorhanden: a) Suche Knoten v, zu dem keine Kante führt (Falls nicht vorhanden keine topologische Sortierung

Mehr

Wir arbeiten mit Zufallszahlen

Wir arbeiten mit Zufallszahlen Abb. 1: Bei Kartenspielen müssen zu Beginn die Karten zufällig ausgeteilt werden. Wir arbeiten mit Zufallszahlen Jedesmal wenn ein neues Patience-Spiel gestartet wird, muss das Computerprogramm die Karten

Mehr

Softwareentwicklung Schrittweise Verfeinerung, Programmieren üben: Tic-Tac-Toe in Raten

Softwareentwicklung Schrittweise Verfeinerung, Programmieren üben: Tic-Tac-Toe in Raten Mag. iur. Dr. techn. Michael Sonntag Softwareentwicklung Schrittweise Verfeinerung, Programmieren üben: Tic-Tac-Toe in Raten E-Mail: sonntag@fim.uni-linz.ac.at http://www.fim.uni-linz.ac.at/staff/sonntag.htm

Mehr

Übungen 19.01.2012 Programmieren 1 Felix Rohrer. Übungen

Übungen 19.01.2012 Programmieren 1 Felix Rohrer. Übungen Übungen if / else / else if... 2... 2 Aufgabe 2:... 2 Aufgabe 3:... 2 Aufgabe 4:... 2 Aufgabe 5:... 2 Aufgabe 6:... 2 Aufgabe 7:... 3 Aufgabe 8:... 3 Aufgabe 9:... 3 Aufgabe 10:... 3 switch... 4... 4 Aufgabe

Mehr

Arbeiten mit UMLed und Delphi

Arbeiten mit UMLed und Delphi Arbeiten mit UMLed und Delphi Diese Anleitung soll zeigen, wie man Klassen mit dem UML ( Unified Modeling Language ) Editor UMLed erstellt, in Delphi exportiert und dort so einbindet, dass diese (bis auf

Mehr

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen Binäre Bäume 1. Allgemeines Binäre Bäume werden grundsätzlich verwendet, um Zahlen der Größe nach, oder Wörter dem Alphabet nach zu sortieren. Dem einfacheren Verständnis zu Liebe werde ich mich hier besonders

Mehr

Erstellen von x-y-diagrammen in OpenOffice.calc

Erstellen von x-y-diagrammen in OpenOffice.calc Erstellen von x-y-diagrammen in OpenOffice.calc In dieser kleinen Anleitung geht es nur darum, aus einer bestehenden Tabelle ein x-y-diagramm zu erzeugen. D.h. es müssen in der Tabelle mindestens zwei

Mehr

SEP 114. Design by Contract

SEP 114. Design by Contract Design by Contract SEP 114 Design by Contract Teile das zu entwickelnde Programm in kleine Einheiten (Klassen, Methoden), die unabhängig voneinander entwickelt und überprüft werden können. Einheiten mit

Mehr

Folge 19 - Bäume. 19.1 Binärbäume - Allgemeines. Grundlagen: Ulrich Helmich: Informatik 2 mit BlueJ - Ein Kurs für die Stufe 12

Folge 19 - Bäume. 19.1 Binärbäume - Allgemeines. Grundlagen: Ulrich Helmich: Informatik 2 mit BlueJ - Ein Kurs für die Stufe 12 Grundlagen: Folge 19 - Bäume 19.1 Binärbäume - Allgemeines Unter Bäumen versteht man in der Informatik Datenstrukturen, bei denen jedes Element mindestens zwei Nachfolger hat. Bereits in der Folge 17 haben

Mehr

Primzahlen und RSA-Verschlüsselung

Primzahlen und RSA-Verschlüsselung Primzahlen und RSA-Verschlüsselung Michael Fütterer und Jonathan Zachhuber 1 Einiges zu Primzahlen Ein paar Definitionen: Wir bezeichnen mit Z die Menge der positiven und negativen ganzen Zahlen, also

Mehr

Es sollte die MS-DOS Eingabeaufforderung starten. Geben Sie nun den Befehl javac ein.

Es sollte die MS-DOS Eingabeaufforderung starten. Geben Sie nun den Befehl javac ein. Schritt 1: Installation des Javacompilers JDK. Der erste Start mit Eclipse Bevor Sie den Java-Compiler installieren sollten Sie sich vergewissern, ob er eventuell schon installiert ist. Gehen sie wie folgt

Mehr

1. Software installieren 2. Software starten. Hilfe zum Arbeiten mit der DÖHNERT FOTOBUCH Software

1. Software installieren 2. Software starten. Hilfe zum Arbeiten mit der DÖHNERT FOTOBUCH Software 1. Software installieren 2. Software starten Hilfe zum Arbeiten mit der DÖHNERT FOTOBUCH Software 3. Auswahl 1. Neues Fotobuch erstellen oder 2. ein erstelltes, gespeichertes Fotobuch laden und bearbeiten.

Mehr

Inhalt. 1 Einleitung AUTOMATISCHE DATENSICHERUNG AUF EINEN CLOUDSPEICHER

Inhalt. 1 Einleitung AUTOMATISCHE DATENSICHERUNG AUF EINEN CLOUDSPEICHER AUTOMATISCHE DATENSICHERUNG AUF EINEN CLOUDSPEICHER Inhalt 1 Einleitung... 1 2 Einrichtung der Aufgabe für die automatische Sicherung... 2 2.1 Die Aufgabenplanung... 2 2.2 Der erste Testlauf... 9 3 Problembehebung...

Mehr

5.2 Neue Projekte erstellen

5.2 Neue Projekte erstellen 5.2 Neue Projekte erstellen Das Bearbeiten von bestehenden Projekten und Objekten ist ja nicht schlecht wie aber können Sie neue Objekte hinzufügen oder gar völlig neue Projekte erstellen? Die Antwort

Mehr

Anleitung zur Daten zur Datensicherung und Datenrücksicherung. Datensicherung

Anleitung zur Daten zur Datensicherung und Datenrücksicherung. Datensicherung Anleitung zur Daten zur Datensicherung und Datenrücksicherung Datensicherung Es gibt drei Möglichkeiten der Datensicherung. Zwei davon sind in Ges eingebaut, die dritte ist eine manuelle Möglichkeit. In

Mehr

mysql - Clients MySQL - Abfragen eine serverbasierenden Datenbank

mysql - Clients MySQL - Abfragen eine serverbasierenden Datenbank mysql - Clients MySQL - Abfragen eine serverbasierenden Datenbank In den ersten beiden Abschnitten (rbanken1.pdf und rbanken2.pdf) haben wir uns mit am Ende mysql beschäftigt und kennengelernt, wie man

Mehr

Zeichen bei Zahlen entschlüsseln

Zeichen bei Zahlen entschlüsseln Zeichen bei Zahlen entschlüsseln In diesem Kapitel... Verwendung des Zahlenstrahls Absolut richtige Bestimmung von absoluten Werten Operationen bei Zahlen mit Vorzeichen: Addieren, Subtrahieren, Multiplizieren

Mehr

Und was uns betrifft, da erfinden wir uns einfach gegenseitig.

Und was uns betrifft, da erfinden wir uns einfach gegenseitig. Freier Fall 1 Der einzige Mensch Der einzige Mensch bin ich Der einzige Mensch bin ich an deem ich versuchen kann zu beobachten wie es geht wenn man sich in ihn hineinversetzt. Ich bin der einzige Mensch

Mehr

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen:

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen: VBA Programmierung mit Excel Schleifen 1/6 Erweiterung der Aufgabe Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen: Es müssen also 11 (B L) x 35 = 385 Zellen berücksichtigt

Mehr

Datensicherung. Beschreibung der Datensicherung

Datensicherung. Beschreibung der Datensicherung Datensicherung Mit dem Datensicherungsprogramm können Sie Ihre persönlichen Daten problemlos Sichern. Es ist möglich eine komplette Datensicherung durchzuführen, aber auch nur die neuen und geänderten

Mehr

Programmierkurs Java

Programmierkurs Java Programmierkurs Java Dr. Dietrich Boles Aufgaben zu UE16-Rekursion (Stand 09.12.2011) Aufgabe 1: Implementieren Sie in Java ein Programm, das solange einzelne Zeichen vom Terminal einliest, bis ein #-Zeichen

Mehr

Leichte-Sprache-Bilder

Leichte-Sprache-Bilder Leichte-Sprache-Bilder Reinhild Kassing Information - So geht es 1. Bilder gucken 2. anmelden für Probe-Bilder 3. Bilder bestellen 4. Rechnung bezahlen 5. Bilder runterladen 6. neue Bilder vorschlagen

Mehr

Informatik 2 Labor 2 Programmieren in MATLAB Georg Richter

Informatik 2 Labor 2 Programmieren in MATLAB Georg Richter Informatik 2 Labor 2 Programmieren in MATLAB Georg Richter Aufgabe 3: Konto Um Geldbeträge korrekt zu verwalten, sind zwecks Vermeidung von Rundungsfehlern entweder alle Beträge in Cents umzuwandeln und

Mehr

infach Geld FBV Ihr Weg zum finanzellen Erfolg Florian Mock

infach Geld FBV Ihr Weg zum finanzellen Erfolg Florian Mock infach Ihr Weg zum finanzellen Erfolg Geld Florian Mock FBV Die Grundlagen für finanziellen Erfolg Denn Sie müssten anschließend wieder vom Gehaltskonto Rückzahlungen in Höhe der Entnahmen vornehmen, um

Mehr

Übungen zu Einführung in die Informatik: Programmierung und Software-Entwicklung: Lösungsvorschlag

Übungen zu Einführung in die Informatik: Programmierung und Software-Entwicklung: Lösungsvorschlag Ludwig-Maximilians-Universität München WS 2015/16 Institut für Informatik Übungsblatt 9 Prof. Dr. R. Hennicker, A. Klarl Übungen zu Einführung in die Informatik: Programmierung und Software-Entwicklung:

Mehr

Übungen für Woche 10

Übungen für Woche 10 Übungen für Woche 10 Martin Rubey 12. Januar 2011 Die folgenden Übungen sollen den Umgang mit Backtracking und kombinatorischen Spezies näherbringen. Genaue Hinweise gibt es erst auf Seite 5. Zur Erinnerung:

Mehr

Das sogenannte Beamen ist auch in EEP möglich ohne das Zusatzprogramm Beamer. Zwar etwas umständlicher aber es funktioniert

Das sogenannte Beamen ist auch in EEP möglich ohne das Zusatzprogramm Beamer. Zwar etwas umständlicher aber es funktioniert Beamen in EEP Das sogenannte Beamen ist auch in EEP möglich ohne das Zusatzprogramm Beamer. Zwar etwas umständlicher aber es funktioniert Zuerst musst du dir 2 Programme besorgen und zwar: Albert, das

Mehr

Es gilt das gesprochene Wort. Anrede

Es gilt das gesprochene Wort. Anrede Sperrfrist: 28. November 2007, 13.00 Uhr Es gilt das gesprochene Wort Statement des Staatssekretärs im Bayerischen Staatsministerium für Unterricht und Kultus, Karl Freller, anlässlich des Pressegesprächs

Mehr

Outlook. sysplus.ch outlook - mail-grundlagen Seite 1/8. Mail-Grundlagen. Posteingang

Outlook. sysplus.ch outlook - mail-grundlagen Seite 1/8. Mail-Grundlagen. Posteingang sysplus.ch outlook - mail-grundlagen Seite 1/8 Outlook Mail-Grundlagen Posteingang Es gibt verschiedene Möglichkeiten, um zum Posteingang zu gelangen. Man kann links im Outlook-Fenster auf die Schaltfläche

Mehr

Diese Ansicht erhalten Sie nach der erfolgreichen Anmeldung bei Wordpress.

Diese Ansicht erhalten Sie nach der erfolgreichen Anmeldung bei Wordpress. Anmeldung http://www.ihredomain.de/wp-admin Dashboard Diese Ansicht erhalten Sie nach der erfolgreichen Anmeldung bei Wordpress. Das Dashboard gibt Ihnen eine kurze Übersicht, z.b. Anzahl der Beiträge,

Mehr

Erstellen einer Collage. Zuerst ein leeres Dokument erzeugen, auf dem alle anderen Bilder zusammengefügt werden sollen (über [Datei] > [Neu])

Erstellen einer Collage. Zuerst ein leeres Dokument erzeugen, auf dem alle anderen Bilder zusammengefügt werden sollen (über [Datei] > [Neu]) 3.7 Erstellen einer Collage Zuerst ein leeres Dokument erzeugen, auf dem alle anderen Bilder zusammengefügt werden sollen (über [Datei] > [Neu]) Dann Größe des Dokuments festlegen beispielsweise A4 (weitere

Mehr

Tevalo Handbuch v 1.1 vom 10.11.2011

Tevalo Handbuch v 1.1 vom 10.11.2011 Tevalo Handbuch v 1.1 vom 10.11.2011 Inhalt Registrierung... 3 Kennwort vergessen... 3 Startseite nach dem Login... 4 Umfrage erstellen... 4 Fragebogen Vorschau... 7 Umfrage fertigstellen... 7 Öffentliche

Mehr

Tutorial: Entlohnungsberechnung erstellen mit LibreOffice Calc 3.5

Tutorial: Entlohnungsberechnung erstellen mit LibreOffice Calc 3.5 Tutorial: Entlohnungsberechnung erstellen mit LibreOffice Calc 3.5 In diesem Tutorial will ich Ihnen zeigen, wie man mit LibreOffice Calc 3.5 eine einfache Entlohnungsberechnung erstellt, wobei eine automatische

Mehr

Qt-Projekte mit Visual Studio 2005

Qt-Projekte mit Visual Studio 2005 Qt-Projekte mit Visual Studio 2005 Benötigte Programme: Visual Studio 2005 Vollversion, Microsoft Qt 4 Open Source s. Qt 4-Installationsanleitung Tabelle 1: Benötigte Programme für die Qt-Programmierung

Mehr

Professionelle Seminare im Bereich MS-Office

Professionelle Seminare im Bereich MS-Office Serienbrief aus Outlook heraus Schritt 1 Zuerst sollten Sie die Kontakte einblenden, damit Ihnen der Seriendruck zur Verfügung steht. Schritt 2 Danach wählen Sie bitte Gerhard Grünholz 1 Schritt 3 Es öffnet

Mehr

Java: Vererbung. Teil 3: super() www.informatikzentrale.de

Java: Vererbung. Teil 3: super() www.informatikzentrale.de Java: Vererbung Teil 3: super() Konstruktor und Vererbung Kindklasse ruft SELBSTSTÄNDIG und IMMER zuerst den Konstruktor der Elternklasse auf! Konstruktor und Vererbung Kindklasse ruft SELBSTSTÄNDIG und

Mehr

Kapitel 4 Die Datenbank Kuchenbestellung Seite 1

Kapitel 4 Die Datenbank Kuchenbestellung Seite 1 Kapitel 4 Die Datenbank Kuchenbestellung Seite 1 4 Die Datenbank Kuchenbestellung In diesem Kapitel werde ich die Theorie aus Kapitel 2 Die Datenbank Buchausleihe an Hand einer weiteren Datenbank Kuchenbestellung

Mehr

Access [basics] Rechnen in Berichten. Beispieldatenbank. Datensatzweise berechnen. Berechnung im Textfeld. Reporting in Berichten Rechnen in Berichten

Access [basics] Rechnen in Berichten. Beispieldatenbank. Datensatzweise berechnen. Berechnung im Textfeld. Reporting in Berichten Rechnen in Berichten Berichte bieten die gleichen Möglichkeit zur Berechnung von Werten wie Formulare und noch einige mehr. Im Gegensatz zu Formularen bieten Berichte die Möglichkeit, eine laufende Summe zu bilden oder Berechnungen

Mehr

Durchführung der Datenübernahme nach Reisekosten 2011

Durchführung der Datenübernahme nach Reisekosten 2011 Durchführung der Datenübernahme nach Reisekosten 2011 1. Starten Sie QuickSteuer Deluxe 2010. Rufen Sie anschließend über den Menüpunkt /Extras/Reisekosten Rechner den QuickSteuer Deluxe 2010 Reisekosten-Rechner,

Mehr

Speicher in der Cloud

Speicher in der Cloud Speicher in der Cloud Kostenbremse, Sicherheitsrisiko oder Basis für die unternehmensweite Kollaboration? von Cornelius Höchel-Winter 2013 ComConsult Research GmbH, Aachen 3 SYNCHRONISATION TEUFELSZEUG

Mehr

Das Leitbild vom Verein WIR

Das Leitbild vom Verein WIR Das Leitbild vom Verein WIR Dieses Zeichen ist ein Gütesiegel. Texte mit diesem Gütesiegel sind leicht verständlich. Leicht Lesen gibt es in drei Stufen. B1: leicht verständlich A2: noch leichter verständlich

Mehr

Microsoft Access 2013 Navigationsformular (Musterlösung)

Microsoft Access 2013 Navigationsformular (Musterlösung) Hochschulrechenzentrum Justus-Liebig-Universität Gießen Microsoft Access 2013 Navigationsformular (Musterlösung) Musterlösung zum Navigationsformular (Access 2013) Seite 1 von 5 Inhaltsverzeichnis Vorbemerkung...

Mehr

! " # $ " % & Nicki Wruck worldwidewruck 08.02.2006

!  # $  % & Nicki Wruck worldwidewruck 08.02.2006 !"# $ " %& Nicki Wruck worldwidewruck 08.02.2006 Wer kennt die Problematik nicht? Die.pst Datei von Outlook wird unübersichtlich groß, das Starten und Beenden dauert immer länger. Hat man dann noch die.pst

Mehr

Folgeanleitung für Klassenlehrer

Folgeanleitung für Klassenlehrer Folgeanleitung für Klassenlehrer 1. Das richtige Halbjahr einstellen Stellen sie bitte zunächst das richtige Schul- und Halbjahr ein. Ist das korrekte Schul- und Halbjahr eingestellt, leuchtet die Fläche

Mehr

Guide DynDNS und Portforwarding

Guide DynDNS und Portforwarding Guide DynDNS und Portforwarding Allgemein Um Geräte im lokalen Netzwerk von überall aus über das Internet erreichen zu können, kommt man um die Themen Dynamik DNS (kurz DynDNS) und Portweiterleitung(auch

Mehr

Was meinen die Leute eigentlich mit: Grexit?

Was meinen die Leute eigentlich mit: Grexit? Was meinen die Leute eigentlich mit: Grexit? Grexit sind eigentlich 2 Wörter. 1. Griechenland 2. Exit Exit ist ein englisches Wort. Es bedeutet: Ausgang. Aber was haben diese 2 Sachen mit-einander zu tun?

Mehr

pro4controlling - Whitepaper [DEU] Whitepaper zur CfMD-Lösung pro4controlling Seite 1 von 9

pro4controlling - Whitepaper [DEU] Whitepaper zur CfMD-Lösung pro4controlling Seite 1 von 9 Whitepaper zur CfMD-Lösung pro4controlling Seite 1 von 9 1 Allgemeine Beschreibung "Was war geplant, wo stehen Sie jetzt und wie könnte es noch werden?" Das sind die typischen Fragen, mit denen viele Unternehmer

Mehr

Informationsblatt Induktionsbeweis

Informationsblatt Induktionsbeweis Sommer 015 Informationsblatt Induktionsbeweis 31. März 015 Motivation Die vollständige Induktion ist ein wichtiges Beweisverfahren in der Informatik. Sie wird häufig dazu gebraucht, um mathematische Formeln

Mehr

Wie halte ich Ordnung auf meiner Festplatte?

Wie halte ich Ordnung auf meiner Festplatte? Wie halte ich Ordnung auf meiner Festplatte? Was hältst du von folgender Ordnung? Du hast zu Hause einen Schrank. Alles was dir im Wege ist, Zeitungen, Briefe, schmutzige Wäsche, Essensreste, Küchenabfälle,

Mehr

Handbuch Fischertechnik-Einzelteiltabelle V3.7.3

Handbuch Fischertechnik-Einzelteiltabelle V3.7.3 Handbuch Fischertechnik-Einzelteiltabelle V3.7.3 von Markus Mack Stand: Samstag, 17. April 2004 Inhaltsverzeichnis 1. Systemvorraussetzungen...3 2. Installation und Start...3 3. Anpassen der Tabelle...3

Mehr

Alle gehören dazu. Vorwort

Alle gehören dazu. Vorwort Alle gehören dazu Alle sollen zusammen Sport machen können. In diesem Text steht: Wie wir dafür sorgen wollen. Wir sind: Der Deutsche Olympische Sport-Bund und die Deutsche Sport-Jugend. Zu uns gehören

Mehr

Übungen zu Einführung in die Informatik: Programmierung und Software-Entwicklung: Lösungsvorschlag

Übungen zu Einführung in die Informatik: Programmierung und Software-Entwicklung: Lösungsvorschlag Ludwig-Maximilians-Universität München WS 2015/16 Institut für Informatik Übungsblatt 13 Prof. Dr. R. Hennicker, A. Klarl Übungen zu Einführung in die Informatik: Programmierung und Software-Entwicklung:

Mehr

Die Post hat eine Umfrage gemacht

Die Post hat eine Umfrage gemacht Die Post hat eine Umfrage gemacht Bei der Umfrage ging es um das Thema: Inklusion Die Post hat Menschen mit Behinderung und Menschen ohne Behinderung gefragt: Wie zufrieden sie in dieser Gesellschaft sind.

Mehr

Der Klassenrat entscheidet

Der Klassenrat entscheidet Folie zum Einstieg: Die Klasse 8c (Goethe-Gymnasium Gymnasium in Köln) plant eine Klassenfahrt: A Sportcamp an der deutschen Nordseeküste B Ferienanlage in Süditalien Hintergrundinfos zur Klasse 8c: -

Mehr

Das Persönliche Budget in verständlicher Sprache

Das Persönliche Budget in verständlicher Sprache Das Persönliche Budget in verständlicher Sprache Das Persönliche Budget mehr Selbstbestimmung, mehr Selbstständigkeit, mehr Selbstbewusstsein! Dieser Text soll den behinderten Menschen in Westfalen-Lippe,

Mehr

Mehr Geld verdienen! Lesen Sie... Peter von Karst. Ihre Leseprobe. der schlüssel zum leben. So gehen Sie konkret vor!

Mehr Geld verdienen! Lesen Sie... Peter von Karst. Ihre Leseprobe. der schlüssel zum leben. So gehen Sie konkret vor! Peter von Karst Mehr Geld verdienen! So gehen Sie konkret vor! Ihre Leseprobe Lesen Sie...... wie Sie mit wenigen, aber effektiven Schritten Ihre gesteckten Ziele erreichen.... wie Sie die richtigen Entscheidungen

Mehr

TYPO3-Zusatzkurs für www.durlacher.de

TYPO3-Zusatzkurs für www.durlacher.de TYPO3-Zusatzkurs für www.durlacher.de In diesem Zusatzkurs (Kapitel 14 bis 18) gehen wir die Dinge an, die im alltäglichen Umgang mit TYPO3 auf www.durlacher.de hilfreich sind. Verschieben, Löschen, Blind

Mehr

Kreativ visualisieren

Kreativ visualisieren Kreativ visualisieren Haben Sie schon einmal etwas von sogenannten»sich selbst erfüllenden Prophezeiungen«gehört? Damit ist gemeint, dass ein Ereignis mit hoher Wahrscheinlichkeit eintritt, wenn wir uns

Mehr

Einrichten einer Festplatte mit FDISK unter Windows 95/98/98SE/Me

Einrichten einer Festplatte mit FDISK unter Windows 95/98/98SE/Me Einrichten einer Festplatte mit FDISK unter Windows 95/98/98SE/Me Bevor Sie die Platte zum ersten Mal benutzen können, muss sie noch partitioniert und formatiert werden! Vorher zeigt sich die Festplatte

Mehr

Beschreibung E-Mail Regeln z.b. Abwesenheitsmeldung und Weiterleitung

Beschreibung E-Mail Regeln z.b. Abwesenheitsmeldung und Weiterleitung Outlook Weiterleitungen & Abwesenheitsmeldungen Seite 1 von 6 Beschreibung E-Mail Regeln z.b. Abwesenheitsmeldung und Weiterleitung Erstellt: Quelle: 3.12.09/MM \\rsiag-s3aad\install\vnc\email Weiterleitung

Mehr

Nach der Installation kann es auch schon losgehen. Für unseren Port Scanner erstellen wir zunächst ein neues Projekt:

Nach der Installation kann es auch schon losgehen. Für unseren Port Scanner erstellen wir zunächst ein neues Projekt: Ein Port Scanner ist eine gute Möglichkeit den eigenen Server auf offene Ports zu scannen. Zu viele nicht benötigte und offene Ports können auf Ihrem Server und auf Ihrem Computer ein Sicherheitsrisiko

Mehr

Reporting Services und SharePoint 2010 Teil 1

Reporting Services und SharePoint 2010 Teil 1 Reporting Services und SharePoint 2010 Teil 1 Abstract Bei der Verwendung der Reporting Services in Zusammenhang mit SharePoint 2010 stellt sich immer wieder die Frage bei der Installation: Wo und Wie?

Mehr

Catherina Lange, Heimbeiräte und Werkstatträte-Tagung, November 2013 1

Catherina Lange, Heimbeiräte und Werkstatträte-Tagung, November 2013 1 Catherina Lange, Heimbeiräte und Werkstatträte-Tagung, November 2013 1 Darum geht es heute: Was ist das Persönliche Geld? Was kann man damit alles machen? Wie hoch ist es? Wo kann man das Persönliche Geld

Mehr

1. Was ihr in dieser Anleitung

1. Was ihr in dieser Anleitung Leseprobe 1. Was ihr in dieser Anleitung erfahren könnt 2 Liebe Musiker, in diesem PDF erhaltet ihr eine Anleitung, wie ihr eure Musik online kostenlos per Werbevideo bewerben könnt, ohne dabei Geld für

Mehr

5 DATEN. 5.1. Variablen. Variablen können beliebige Werte zugewiesen und im Gegensatz zu

5 DATEN. 5.1. Variablen. Variablen können beliebige Werte zugewiesen und im Gegensatz zu Daten Makro + VBA effektiv 5 DATEN 5.1. Variablen Variablen können beliebige Werte zugewiesen und im Gegensatz zu Konstanten jederzeit im Programm verändert werden. Als Variablen können beliebige Zeichenketten

Mehr

Einführung in die Java- Programmierung

Einführung in die Java- Programmierung Einführung in die Java- Programmierung Dr. Volker Riediger Tassilo Horn riediger horn@uni-koblenz.de WiSe 2012/13 1 Wichtig... Mittags keine Pommes... Praktikum A 230 C 207 (Madeleine + Esma) F 112 F 113

Mehr

Was ist Sozial-Raum-Orientierung?

Was ist Sozial-Raum-Orientierung? Was ist Sozial-Raum-Orientierung? Dr. Wolfgang Hinte Universität Duisburg-Essen Institut für Stadt-Entwicklung und Sozial-Raum-Orientierte Arbeit Das ist eine Zusammen-Fassung des Vortrages: Sozialräume

Mehr

M03a Lernstraße für den Unterricht in Sekundarstufe I

M03a Lernstraße für den Unterricht in Sekundarstufe I M03a Lernstraße für den Unterricht in Sekundarstufe I 1. Station: Der Taufspruch Jedem Täufling wird bei der Taufe ein Taufspruch mit auf den Weg gegeben. Dabei handelt es sich um einen Vers aus der Bibel.

Mehr

Die druckfähige pdf-version ist zu laden von lernelesen.com/bedienungsanleitung.htm

Die druckfähige pdf-version ist zu laden von lernelesen.com/bedienungsanleitung.htm 1 Die druckfähige pdf-version ist zu laden von lernelesen.com/bedienungsanleitung.htm Anleitung LeLe_S1 ------------------- Diese App ist inhaltlich gleich mit LeLe_1. Nur die Darstellung und der Zugriff

Mehr

Nina. bei der Hörgeräte-Akustikerin. Musterexemplar

Nina. bei der Hörgeräte-Akustikerin. Musterexemplar Nina bei der Hörgeräte-Akustikerin Nina bei der Hörgeräte-Akustikerin Herausgeber: uphoff pr-consulting Alfred-Wegener-Str. 6 35039 Marburg Tel.: 0 64 21 / 4 07 95-0 info@uphoff-pr.de www.uphoff-pr.de

Mehr

Handbuch ECDL 2003 Basic Modul 5: Datenbank Grundlagen von relationalen Datenbanken

Handbuch ECDL 2003 Basic Modul 5: Datenbank Grundlagen von relationalen Datenbanken Handbuch ECDL 2003 Basic Modul 5: Datenbank Grundlagen von relationalen Datenbanken Dateiname: ecdl5_01_00_documentation_standard.doc Speicherdatum: 14.02.2005 ECDL 2003 Basic Modul 5 Datenbank - Grundlagen

Mehr

ZfP-Sonderpreis der DGZfP beim Regionalwettbewerb Jugend forscht BREMERHAVEN. Der Zauberwürfel-Roboter. Paul Giese. Schule: Wilhelm-Raabe-Schule

ZfP-Sonderpreis der DGZfP beim Regionalwettbewerb Jugend forscht BREMERHAVEN. Der Zauberwürfel-Roboter. Paul Giese. Schule: Wilhelm-Raabe-Schule ZfP-Sonderpreis der DGZfP beim Regionalwettbewerb Jugend forscht BREMERHAVEN Der Zauberwürfel-Roboter Paul Giese Schule: Wilhelm-Raabe-Schule Jugend forscht 2013 Kurzfassung Regionalwettbewerb Bremerhaven

Mehr

Leitfaden zur ersten Nutzung der R FOM Portable-Version für Windows (Version 1.0)

Leitfaden zur ersten Nutzung der R FOM Portable-Version für Windows (Version 1.0) Leitfaden zur ersten Nutzung der R FOM Portable-Version für Windows (Version 1.0) Peter Koos 03. Dezember 2015 0 Inhaltsverzeichnis 1 Voraussetzung... 3 2 Hintergrundinformationen... 3 2.1 Installationsarten...

Mehr

Die Captimizer BTZ-Datei 2015

Die Captimizer BTZ-Datei 2015 Dipl.-Math. Rainer Schwindt Captimizer s Secrets behind the User Interface 2 Die Captimizer BTZ-Datei 2015 Regeln zur BTZ bei laufendem Navigator und Navigator-Neustart beim Jahreswechsel Geheimnisse hinter

Mehr

Windows. Workshop Internet-Explorer: Arbeiten mit Favoriten, Teil 1

Windows. Workshop Internet-Explorer: Arbeiten mit Favoriten, Teil 1 Workshop Internet-Explorer: Arbeiten mit Favoriten, Teil 1 Wenn der Name nicht gerade www.buch.de oder www.bmw.de heißt, sind Internetadressen oft schwer zu merken Deshalb ist es sinnvoll, die Adressen

Mehr

Was ich als Bürgermeister für Lübbecke tun möchte

Was ich als Bürgermeister für Lübbecke tun möchte Wahlprogramm in leichter Sprache Was ich als Bürgermeister für Lübbecke tun möchte Hallo, ich bin Dirk Raddy! Ich bin 47 Jahre alt. Ich wohne in Hüllhorst. Ich mache gerne Sport. Ich fahre gerne Ski. Ich

Mehr

Computeria Rorschach Mit Excel Diagramme erstellen

Computeria Rorschach Mit Excel Diagramme erstellen Mit Excel Diagramme erstellen 25.12.2010 Roland Liebing Mit Excel Diagramme erstellen Diagramme können Zahlenwerte veranschaulichen, das heisst, mit Hilfe eines Diagramms können Zahlen besser miteinander

Mehr

Im Folgenden wird Ihnen an einem Beispiel erklärt, wie Sie Excel-Anlagen und Excel-Vorlagen erstellen können.

Im Folgenden wird Ihnen an einem Beispiel erklärt, wie Sie Excel-Anlagen und Excel-Vorlagen erstellen können. Excel-Schnittstelle Im Folgenden wird Ihnen an einem Beispiel erklärt, wie Sie Excel-Anlagen und Excel-Vorlagen erstellen können. Voraussetzung: Microsoft Office Excel ab Version 2000 Zum verwendeten Beispiel:

Mehr

Nicht kopieren. Der neue Report von: Stefan Ploberger. 1. Ausgabe 2003

Nicht kopieren. Der neue Report von: Stefan Ploberger. 1. Ausgabe 2003 Nicht kopieren Der neue Report von: Stefan Ploberger 1. Ausgabe 2003 Herausgeber: Verlag Ploberger & Partner 2003 by: Stefan Ploberger Verlag Ploberger & Partner, Postfach 11 46, D-82065 Baierbrunn Tel.

Mehr

Komponententest. Testen von Software Systemen. Übung 02 SS 2009 Version: 1.0 09.06.2009

Komponententest. Testen von Software Systemen. Übung 02 SS 2009 Version: 1.0 09.06.2009 Testen von Software Systemen Übung 02 SS 2009 Version: 1.0 09.06.2009 Komponententest Kunde: Dr. Reinhold Plösch Dr. Johannes Sametinger Kundenreferenz: 259.019 Team 19 Mitarbeiter: Christian Märzinger

Mehr

Dokumentation zum Spielserver der Software Challenge

Dokumentation zum Spielserver der Software Challenge Dokumentation zum Spielserver der Software Challenge 10.08.2011 Inhaltsverzeichnis: Programmoberfläche... 2 Ein neues Spiel erstellen... 2 Spielfeldoberfläche... 4 Spielwiederholung laden... 5 Testdurchläufe...

Mehr

Lernerfolge sichern - Ein wichtiger Beitrag zu mehr Motivation

Lernerfolge sichern - Ein wichtiger Beitrag zu mehr Motivation Lernerfolge sichern - Ein wichtiger Beitrag zu mehr Motivation Einführung Mit welchen Erwartungen gehen Jugendliche eigentlich in ihre Ausbildung? Wir haben zu dieser Frage einmal die Meinungen von Auszubildenden

Mehr

Kostenstellen verwalten. Tipps & Tricks

Kostenstellen verwalten. Tipps & Tricks Tipps & Tricks INHALT SEITE 1.1 Kostenstellen erstellen 3 13 1.3 Zugriffsberechtigungen überprüfen 30 2 1.1 Kostenstellen erstellen Mein Profil 3 1.1 Kostenstellen erstellen Kostenstelle(n) verwalten 4

Mehr

Text-Zahlen-Formatieren

Text-Zahlen-Formatieren Text-Zahlen-Formatieren Beobachtung: Bei der Formatierung einer Zahl in eine Textzahl und umgekehrt zeigt Excel ein merkwürdiges Verhalten, welches nachfolgend skizziert werden soll: Wir öffnen eine neue

Mehr

Erklärung zum Internet-Bestellschein

Erklärung zum Internet-Bestellschein Erklärung zum Internet-Bestellschein Herzlich Willkommen bei Modellbahnbau Reinhardt. Auf den nächsten Seiten wird Ihnen mit hilfreichen Bildern erklärt, wie Sie den Internet-Bestellschein ausfüllen und

Mehr

Inhalt: Ihre persönliche Sedcard... 1 Login... 1 Passwort vergessen... 2 Profildaten bearbeiten... 3

Inhalt: Ihre persönliche Sedcard... 1 Login... 1 Passwort vergessen... 2 Profildaten bearbeiten... 3 Inhalt: Ihre persönliche Sedcard..... 1 Login... 1 Passwort vergessen... 2 Profildaten bearbeiten... 3 Passwort ändern... 3 email ändern... 4 Sedcard-Daten bearbeiten... 4 Logout... 7 Ich kann die Sedcard

Mehr

Was man mit dem Computer alles machen kann

Was man mit dem Computer alles machen kann Was man mit dem Computer alles machen kann Wie komme ich ins Internet? Wenn Sie einen Computer zu Hause haben. Wenn Sie das Internet benutzen möchten, dann brauchen Sie ein eigenes Programm dafür. Dieses

Mehr

Wichtig ist die Originalsatzung. Nur was in der Originalsatzung steht, gilt. Denn nur die Originalsatzung wurde vom Gericht geprüft.

Wichtig ist die Originalsatzung. Nur was in der Originalsatzung steht, gilt. Denn nur die Originalsatzung wurde vom Gericht geprüft. Das ist ein Text in leichter Sprache. Hier finden Sie die wichtigsten Regeln für den Verein zur Förderung der Autonomie Behinderter e. V.. Das hier ist die Übersetzung der Originalsatzung. Es wurden nur

Mehr

Übung 1 mit C# 6.0 MATTHIAS RONCORONI

Übung 1 mit C# 6.0 MATTHIAS RONCORONI Übung 1 mit C# 6.0 MATTHIAS RONCORONI Inhalt 2 1. Überblick über C# 2. Lösung der Übung 1 3. Code 4. Demo C# allgemein 3 aktuell: C# 6.0 mit.net-framework 4.6: Multiparadigmatisch (Strukturiert, Objektorientiert,

Mehr

Anwendungspraktikum aus JAVA Programmierung im SS 2006 Leitung: Albert Weichselbraun. Java Projekt. Schiffe Versenken mit GUI

Anwendungspraktikum aus JAVA Programmierung im SS 2006 Leitung: Albert Weichselbraun. Java Projekt. Schiffe Versenken mit GUI Anwendungspraktikum aus JAVA Programmierung im SS 2006 Leitung: Albert Weichselbraun Java Projekt Schiffe Versenken mit GUI 1. Über den Autor: Name: Marija Matejic Matrikelnummer: 9352571 E-mail: marijamatejic@yahoo.com

Mehr