Die Kombination von funktionalen und objektorientierten Sprachelementen mit F# und C#
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
Die Kombination von funktionalen und objektorientierten Sprachelementen mit F# und C# Bachelorarbeit zur Erlangung des akademischen Grades BACHELOR OF SCIENCE (B.SC.) im Studiengang Allgemeine Informatik vorgelegt an der Fachhochschule Köln Campus Gummersbach Fakultät für Informatik und Ingenieurwissenschaften ausgearbeitet von: Michael Morbach Erstprüfer/in: Prof. Dr. Erich Ehses (Fachhochschule Köln) Zweitprüfer/in: Prof. Dr. Victor (Fachhochschule Köln) Gummersbach, im Juli 2013
I Kurzfassung Kurzfassung Die vorliegende Bachelorarbeit beschäftigt sich vorwiegend mit der Fragestellung in- wieweit sich die Paradigmen der funktionalen und der objektorientierten Programmie- rung in der heutigen Softwareentwicklung im .NET Umfeld kombinieren lassen und wie sie sich bereits darin äußern. Eine ausführliche Einführung in die funktionale Program- mierung soll dazu beitragen, einen Einblick in die Konzepte und die Sichtweise des funktionalen Entwickelns und Denkens zu erlangen. Diese Einblicke werden durch den Entwicklungsprozess einer Anwendung bestärkt, der praxisnah darstellt, in welchen Bereichen sich die beiden Paradigmen ergänzen können. Abstract This bachelor thesis deals mainly with the question how far it is practically possible to combine the paradigms of functional- and object oriented programming in a .NET envi- ronment and how they do manifest themselves already. A detailed introduction to functional programming should help to gain an insight into the functional concepts and thinking. These insights are strengthened by the development process of an application, which demonstrates where functional and object oriented programming complement each other.
II Inhaltsverzeichnis Inhaltsverzeichnis Kurzfassung ...................................................................................................................... I Abstract............................................................................................................................. I Inhaltsverzeichnis .......................................................................................................... II Abbildungsverzeichnis................................................................................................... V Abkürzungsverzeichnis ................................................................................................ VI 1 Einleitung ............................................................................................................. 1 2 Grundlagen der funktionalen Programmierung .............................................. 2 2.1 Was ist funktionale Programmierung? .................................................................. 2 2.2 Funktionale Programmierung produktiv einsetzen ............................................... 3 2.2.1 Paradigma der funktionalen Programmierung ....................................................... 3 2.2.2 Deklarativer Programmierstil ................................................................................ 3 2.2.3 Verstehen was ein Programm tut ........................................................................... 5 2.2.4 Nebenläufigkeit ..................................................................................................... 6 2.3 Beispiele funktionaler Programmierung................................................................ 7 2.3.1 Das Verarbeiten von Daten mit LINQ ................................................................... 7 2.3.2 Beschreibung einer Benutzeroberfläche mit XAML ............................................. 8 2.3.3 Komposition .......................................................................................................... 9 3 Kernkonzepte von F# ........................................................................................ 11 3.1 Auswertung funktionaler Programme ................................................................. 11 3.1.1 Verwenden von unveränderlichen Datenstrukturen ............................................ 11 3.1.2 Den Zustand eines Programms durch Rekursion verändern ............................... 12 3.1.3 Verwenden von Ausdrücken an Stelle von Anweisungen .................................. 13 3.2 Schreiben von deklarativem Code ....................................................................... 14 3.2.1 Funktionen als Argumente .................................................................................. 14 3.2.2 Funktionen höherer Ordnung .............................................................................. 15 3.3 Typen in der funktionalen Programmierung ....................................................... 16 4 Anwendungskonzept Pixxler ............................................................................ 18 4.1 Serviceorientierte Architektur ............................................................................. 18 4.2 WCF Service Pixxler ........................................................................................... 19 4.3 Pixxler API .......................................................................................................... 19 4.4 Clientanwendung Pixxler .................................................................................... 20 4.5 F# Bibliothek ....................................................................................................... 20
III Inhaltsverzeichnis 5 Implementierung des Pixxler Services............................................................. 21 5.1 Was ist ein WCF Service ..................................................................................... 21 5.2 Einrichten der Datenbank .................................................................................... 22 5.3 Ansteuerung der Datenbank aus dem Code......................................................... 23 5.3.1 Einsatz des Entity Frameworks zur Abbildung des Datenbankmodells im Code ..................................................................................................................... 24 5.3.2 Was ist LINQ? ..................................................................................................... 25 5.4 Authentifizierung eines Benutzers ...................................................................... 27 5.4.1 Annotation von Klassen und Methoden .............................................................. 27 5.4.2 Schnittstellendefinition zum Authentifizieren ..................................................... 28 5.4.3 Abfragen von Daten aus der Datenbank mit LINQ ............................................. 29 5.4.4 Das Repository Entwurfsmuster .......................................................................... 29 5.4.5 Validieren eines Benutzers .................................................................................. 30 6 Funktionale Bibliothek mit F# ......................................................................... 33 6.1 Parallelisierungstechniken ................................................................................... 33 6.1.1 Parallelisieren von kleinen Codesegmenten ........................................................ 33 6.2 Implementierung der grafischen Effekte ............................................................. 34 6.2.1 Berechnungen von Farben ................................................................................... 35 6.2.2 Implementieren und Anwenden von Farbfiltern ................................................. 36 6.2.3 Implementieren eines Unschärfeeffekts .............................................................. 39 6.3 Was kann F# einem C# Entwickler bieten? ........................................................ 40 7 Implementierung des Pixxler WPF Clients ..................................................... 41 7.1 Warum WPF? ...................................................................................................... 41 7.2 Architektur der Anwendung ................................................................................ 42 7.2.1 PRISM ................................................................................................................. 42 7.2.2 Das Architekturmuster MVVM ........................................................................... 44 7.2.3 Dependency Injection .......................................................................................... 45 7.3 Struktur der Anwendung (Shell) ......................................................................... 46 7.4 Implementierung der Module .............................................................................. 48 7.4.1 Anmeldung am Service ....................................................................................... 48 7.4.2 Übersicht über die Alben ..................................................................................... 49 7.4.3 Bearbeiten von Bildern ........................................................................................ 52 8 Fazit .................................................................................................................... 53 8.1 Zusammenfassung ............................................................................................... 53 8.2 Wie hat sich die funktionale Programmierung eingebracht ................................ 54 8.2.1 LINQ zur Abfrage von Datenstrukturen.............................................................. 54 8.2.2 WPF & XAML zur Gestaltung von Oberflächen ................................................ 55 8.2.3 F# zur Implementierung von Kernfunktionalitäten ............................................. 56
IV Inhaltsverzeichnis 8.3 Persönliches Fazit ................................................................................................ 56 9 Literaturverzeichnis .......................................................................................... 58
V Abbildungsverzeichnis Abbildungsverzeichnis Abbildung 1: SOA (Crofton, 2010) ................................................................................ 18 Abbildung 2: WCF - ABC Prinzip ................................................................................ 22 Abbildung 3: ER Modell der Datenbank ........................................................................ 23 Abbildung 4: EF Modell der Datenbank im Code .......................................................... 25 Abbildung 5: Definition des Interfaces für den BenutzerService ................................... 28 Abbildung 6: Das Repository Entwurfsmuster .............................................................. 30 Abbildung 7: Das BenutzerRepository ........................................................................... 30 Abbildung 8: Die DataContracts..................................................................................... 32 Abbildung 9: Modell des PRISM Frameworks ............................................................. 44 Abbildung 10: Modell des MVVM Entwurfsmusters ................................................... 45 Abbildung 11: Struktureller Aufbau des WPF Clients ................................................... 46 Abbildung 12: Login am Service .................................................................................... 48 Abbildung 13: Login Sequenzdiagramm ........................................................................ 49 Abbildung 14: Inhalte eines Albums darstellen.............................................................. 50 Abbildung 15: Ein Bild im Detail aufrufen .................................................................... 51 Abbildung 16: Ein Bild bearbeiten ................................................................................. 52
VI Abkürzungsverzeichnis Abkürzungsverzeichnis API Application Programming Interface CLR Common Language Runtime DI Dependency Injection EF Entity Framework (5.0) GUID Globally Unique Identifier IoC Inversion of Control LINQ Language Integrated Query MVVM Model-View-ViewModel SOAP Simple Object Access Protocol SoC Separation of Concerns UI User Interface WCF Windows Communication Foundation WPF Windows Presentation Foundation XAML Extensible Application Markup Language
1 Einleitung 1 Einleitung Die letzten Jahrzehnte hat die objektorientierte Programmierung den Markt in der Soft- wareentwicklung dominiert, indem sie es erreichte, die Komplexität einer Anwendung mit Struktur zu versehen und Sprachen für Entwickler intuitiv einsetzbar zu machte. Doch nicht jede Art von Komplexität lässt sich mit Hilfe von Objekten abbilden. Einige Bereiche, die z.B. Berechnungen, Analysen oder Transformationen betreffen, ziehen keinen Nutzen aus Objekten. Somit stehen Entwickler in der heutigen Zeit neuen Her- ausforderungen gegenüber. Es müssen Programme entwickelt werden, die eine große Menge an Daten schnell und sparsam verarbeiten können und gleichzeitig auf mehrere Prozessoren zugeschnitten sind. Aus dem Schatten der wissenschaftlichen Institutionen haben sich die funktionalen und deklarativen Techniken der Softwareentwicklung in den vergangenen Jahren mehr und mehr auch in der Wirtschaft bewährt. In C# wurden generics, anonyme Funktionen und Ausdrücke zur Abfrage von Daten eingebaut. Eine ausgereifte kommerzielle funktionale Sprache blieb allerdings bisher aus. Mit F# entwickelte Microsoft seit 2002 eine Spra- che die sowohl funktional ist, zudem aber über das .NET Objektmodell ebenso Zugriff auf objektorientierte Bereiche besitzt. Programmiersprachen in der heutigen Zeit sind multiparadigmatisch. Objektorientierte und funktionale Programmierung geht mehr und mehr in einander über und bietet somit eine weitläufige Liste von einsetzbaren Möglichkeiten bei der Entwicklung von An- wendungen. So kann bereits jetzt in C# auf viele funktionale Techniken zurückgegriffen werden.. Funktionale Programmierung jedoch beschränkt sich nicht nur auf die Art und Weise wie Code geschrieben wird. Mehr ist es eine andere Sichtweise wie Probleme gelöst werden können. In dieser Arbeit sollen diese Betrachtungsweise und einige anwendbare Techniken bei der Entwicklung einer Software, die sowohl das objektorientierte als auch das funktionale Paradigma nutzt, aufgezeigt werden.
2 Grundlagen der funktionalen Programmierung 2 Grundlagen der funktionalen Programmierung Funktionale Sprachen sind Ausdrucksstart und erledigen sogar große Aufgaben, obwohl der dazu geschriebene Code meist minimalistisch ist. Funktionaler Code wird ergebnis- orientierter verfasst und die Art und Weise wie Anweisungen ausgeführt werden, kann besser vor den Entwicklern versteckt werden. Es ist insgesamt unwichtiger worden wie die Ergebnisse erreicht werden, denn dieser Weg zur Ergebnisfindung muss nur ein ein- ziges Mal spezifiziert werden. 2.1 Was ist funktionale Programmierung? Es existiert eine ganze Reihe von unterschiedlichen funktionalen Sprachen und es gibt keine Funktionalitäten die eben jede dieser Sprachen besitzen müsste. Daher ist es schwierig eine allgemeingültige Definition, die auf die funktionale Programmierung zutrifft, zu finden. Was die Sprachen miteinander verbindet sind eher allgemeine As- pekte. Dennoch hat jede dieser Sprachen einen eigenen Stil, um Lösungen für Probleme zu schaffen. Funktionale Programmierung lässt sich daher wohl am besten im Vergleich zur imperativen Programmierung beschreiben. „Functional programming is a style of programming that emphasizes the evaluation of expressions, rather than execution of commands. The expressions in these languages are formed by using functions to combine basic values“ 1 Der funktionale Ansatz wird aus dem ersten Satz gut ersichtlich. Die „Auswertung von Ausdrücken“ repräsentiert den funktionalen Aspekt und die „Ausführung von Anweisun- gen“ den imperativen Aspekt. Ausführung von Anweisungen – Ein Programm wird als eine Verkettung von Be- fehlen ausgedrückt. Die Befehle beschreiben wie das Ende des Programms er- reicht wird und was für Objekte während dieses Prozesses erstellt und verändert werden. Auswertung von Ausdrücken – Ein funktionales Programm entspricht eher einem Ausdruck der beschreibt wie z.B. die Eigenschaften von einem Objekt aussehen 1 (Hutton, 2002) http://www.cs.nott.ac.uk/~gmh/faq.html
3 Grundlagen der funktionalen Programmierung sollen nachdem es erstellt wurde. Es werden keine einzelnen Schritte vorgege- ben, die besagen wie das Objekt zusammengebaut wird und es besteht keine Möglichkeit die Erzeugung eines Objektes zu beeinflussen, bevor es nicht er- stellt wurde. Sollte also z.B. eine Tasse Kaffee mit Zucker erstellen werden, so könnte der Kaffee nicht getrunken werden bevor der Zucker enthalten ist, denn wenn das Objekt erstellt ist enthält es den Zucker bereits. Der wesentliche Unterschied ist also der, dass bei der funktionalen Programmierung Code in Form von Ausdrücken anstatt in einer Reihe von Anweisungen geschrieben wird. Aus diesem Grund wird Code in der funktionalen Programmierung auf eine ganz andere Art und Weise verfasst und gekapselt. 2.2 Funktionale Programmierung produktiv einsetzen Entwickler schätzen die funktionale Programmierung oft durch ihre ausdrucksstarke und lesbare Art. Dies ist jedoch noch lange kein Grund es auch wirklich zu praktizieren. In diesem Kapitel soll der Nutzen und die Vorteile der funktionalen Programmierung herausgestellt werden. 2.2.1 Paradigma der funktionalen Programmierung Funktionales Programmieren ist ein Programmierparadigma. Das bedeutet, dass es Konzepte definiert, die genutzt werden können um bestimmte Probleme zu lösen. In der Objektorientierten Programmierung wird über Problemlösungen in Form von Objekten nachgedacht. Jede Sprache ist im speziellen etwas anders. So bietet C++ die mehrfache Vererbung und JavaScript die Prototypen. Funktional zu programmieren bedeutet je- doch nicht gleichzeitig auf das jeweils andere Paradigma zu verzichten. So bietet die Sprache C#, welche eine im Kern objektorientierte Sprache ist, seit der Version 3.0 auch einige funktionale Möglichkeiten die ausgenutzt werden können. Auf der anderen Seite ist F# eine funktionale Sprache, die jedoch das vollständige .NET Objektmodell unter- stützt. Es kann also in beiden Fällen genau das Paradigma für ein Problem gewählt wer- den, welches für die Lösung am besten geeignet ist. 2.2.2 Deklarativer Programmierstil Beim deklarativen Programmierstil wird die Logik eines Programmes ausgedrückt, ohne auf konkrete Implementierungsdetails näher einzugehen. Diese Beschreibung könnte
4 Grundlagen der funktionalen Programmierung zwar auch auf die Definition der funktionalen Programmierung passen, jedoch ist die Idee der deklarativen Programmierung sehr viel weitläufiger und kann mit vielen unter- schiedlichen Technologien umgesetzt werden. Da sich diese Arbeit unter anderem auf die funktionale Programmierung konzentriert, wird im Folgenden demonstriert wie an- hand einer funktionalen Sprache deklarativer Code geschrieben werden könnte. Anwendungen werden implementiert, indem einem Computer die Ziele anhand eines vorgegebenen Vokabulars, welches dieser interpretieren kann, beschrieben werden. Bei der imperativen Programmierung besteht dieses Vokabular aus einzelnen Befehlen. Es besteht allerdings immer die Möglichkeit neue Befehle, wie z.B. „Zeige Kundende- tails“, hinzuzufügen. Das gesamte Programm ist jedoch lediglich eine Aneinanderrei- hung von einzelnen Schritten, die beschreiben wie eine Gesamtaufgabe erledigt werden soll. Eine Aufgabe könnte zum Beispiel wie folgt lauten: „Nimm den nächsten Kunden aus der Liste. Wenn der Kunde in Deutschland wohnt, dann zeige seine Details. Wenn mehrere Kunden in der Liste vorhanden sind gehe zum Anfang“ Sobald ein Programm wächst, wird die Anzahl an Befehlen in dem Vokabular schnell größer und es wird immer schwieriger dieses zu verwenden. Der objektorientierte An- satz erleichtert dies, weil dieser eine Ordnung und Organisation in die anlaufenden Be- fehle bringt. So könnten alle Befehle, die mit Kunden zu tun haben, in einer Klassen- struktur unterbringen. Das Programm selbst wäre jedoch immer noch eine Sequenz aus Befehlen. Funktionale Programmierung bietet einen ganz anderen Ansatz dieses Vokabular zu erweitern. Anstatt sich darauf zu beschränken nur neue Befehle zu erstellen, wird darauf gesetzt neue Kontrollstrukturen zu erzeugen – Primitive die beschreiben wie Befehle miteinander verknüpft werden können. In imperativen Programmen wird eine Sequenz von Befehlen zusammengestellt, die eine beschränkte Anzahl von eingebauten Kontroll- strukturen wie z.B. Schleifen nutzt. Wenn jedoch ein typisches Programm betrachtet wird, so lässt sich beobachten, dass sich die meisten Strukturen wiederholen. Einige dieser Strukturen sind bekannt und werden Entwurfsmuster genannt. In dem Kundenbeispiel lässt sich ebenfalls ein Muster erkennen. „Führe den ersten Befehl für jeden Kunden aus bei dem der Zweite Befehl ‚wahr‘ zurückgibt“. Diesen Ausdruck kann man auch simplifizieren zu „Zeige die Kundendetails für jeden Kunden
5 Grundlagen der funktionalen Programmierung aus Deutschland“. In diesem Satz ist „Zeige Kundendetails“ das erste Argument und „aus Deutschland“ das Zweite. Beide Sätze zur Lösung desselben Problems werden nun gegenübergestellt: Nimm den nächsten Kunden aus der Liste. Wenn der Kunde in Deutschland wohnt, dann zeige seine Details. Wenn mehrere Kunden in der Liste vorhanden sind gehe zum Anfang Zeige die Kundendetails für jeden Kunden aus Deutschland Es lässt sich klar erkennen, dass im ersten Satz genau beschreiben wird wie das Ziel erreicht werden kann, während der zweite Satz beschreibt was genau erreicht werden soll. Dies ist auch gleichzeitig der essentielle Unterschied zwischen imperativer und deklarativer Programmierung. 2.2.3 Verstehen was ein Programm tut Ein gewöhnliches Programm besteht aus allerhand Objekten die einen internen Zustand haben, der entweder direkt oder durch Methodenaufrufe verändert werden kann. Das bedeutet, dass es sehr schwierig sein kann zu erkennen welche Eigenschaften sich durch einen Methodenaufruf verändert haben. Folgendes Beispiel verdeutlicht dies: Ellipse und Rechteck (C#) Ellipse el = new Ellipse( new Rectangle( 0, 0, 100, 100 ) ); Rectangle rc = el.BoundingBox; rc.Inflate( 10, 10 ); return el; Bei diesem Ausschnitt kann man nicht wissen, welchen Zustand die zurückgegebene Ellipse haben wird, ohne den Code auszuführen. Besitzt die BoundingBox eine Rückre- ferenz zur Ellipse und verändert die Methode Inflate(x,y) das Rechteck oder gibt es ein neues Rechteck zurück? Wäre letzteres der Fall, so hätte die dritte Zeile keinerlei Effekt. Bei der funktionalen Programmierung sind die meisten Datenstrukturen nicht veränder- lich. Somit kann eine erzeugte Ellipse nach ihrer Erzeugung nicht mehr verändert wer- den. Das einzige was getan werden könnte, wäre eine neue Ellipse zu erzeugen und mit anderen Parametern zu initialisieren. Das erleichtert das Lesen von Quelltext erheblich wie im nächsten Beispiel leicht zu erkennen ist.
6 Grundlagen der funktionalen Programmierung Ellipse und Rechteck (C#) Ellipse el = new Ellipse( new Rectangle( 0, 0, 100, 100 ) ); Rectangle rc = el.BoundingBox; Rectangle rcNew = rc.Inflate( 10, 10 ); return new Ellipse( rcNew ); Beim Schreiben von funktionalen Programmen mit unveränderlichen Typen ist das Ein- zige, was eine Methode tun kann, ein Rückgabewert liefern. Wie man im Beispiel er- kennen kann gibt die Methode Inflate(x,y) ein neues Rechteck zurück. Dies ist keine neue Idee in der .NET Welt, denn es existieren auch andere Typen die genauso funktio- nieren, wie z.B. der Typ DateTime oder String. Die Lesbarkeit des Quellcodes wird durch diese Variante maßgeblich verbessert. Die Lesbarkeit ist jedoch nicht der einzige Grund für diesen Ansatz. Ein weiterer Grund ist die Entwicklung von Anwendungen mit Unterstützung für mehrere Kerne. 2.2.4 Nebenläufigkeit Möchte man in der traditionellen imperativen Programmierung eine nebenläufige An- wendung entwickeln, so stößt man auf folgende Probleme: Es ist schwer, existierenden sequenziellen Code in parallel ablaufenden Code umzuwandeln, da eine große Menge an Code explizit so umgeschrieben werden muss, dass Threads verwendet werden. Es muss sehr genau geprüft werden, dass beim Zugriff auf Objekte keine Dead- locks auftreten können und dennoch muss genügend Spielraum zur parallelen Ausführung bestehen bleiben. Die Funktionale Programmierung hat darauf folgende Antworten: Bei Verwendung des deklarativen Programmierstils kann Nebenläufigkeit in be- stehenden Code eingeführt werden. Durch Ersetzen der Primitive, die bestim- men wie Befehle kombiniert werden, ist dies jederzeit möglich. Dank der Unveränderlichkeit von Objekten ist es unmöglich, dass race conditions2 oder deadlocks3 auftreten. Es ist außerdem direkt ersichtlich, welche Teile des Programms unabhängig laufen. 2 Eine Konstellation in der das Ergebnis einer Operation durch andere Einzeloperationen beeinflusst wird 3 Ein Zustand, bei dem mehrere Prozesse auf die Zustandsänderung eines anderen warten und sich gegen- seitig behindern
7 Grundlagen der funktionalen Programmierung Durch diese beiden Aspekte wird das Design einer Anwendung maßgeblich beeinflusst, was zu einer Erleichterung beim Schreiben von parallel ausführbarem Code führt wo- durch auch der Vorteil von Mehrkernprozessoren ausgenutzt werden kann. Es sollte jedoch beachtet werden, dass die Parallelisierung von Code nicht allein durch den Ein- satz von nicht veränderlichem Code eintritt, sondern lediglich erleichtert wird. 2.3 Beispiele funktionaler Programmierung Im Folgenden soll anhand einiger kleinerer Beispiele gezeigt werden, dass funktionale Programmierung nicht nur in der Theorie einsetzbar ist. Es wird außerdem auf die de- klarative Programmierung sowie ihre Vorteile anhand von LINQ und XAML eingegan- gen. 2.3.1 Das Verarbeiten von Daten mit LINQ LINQ wurde von Microsoft entwickelt, um ein einheitliches Verfahren für den Zugriff auf Datenstrukturen verschiedener Arten bereitzustellen. Im Folgenden soll anhand ei- nes Codebeispiels gezeigt werden, welche Vorteile sich durch den deklarativen Pro- grammierstil ergeben. Imperative Datenverarbeitung (C#) public IEnumerable GetExpensiveProducts() { List filteredInfos = new List(); foreach ( Product product in this.Products ) { if ( product.UnitPrice > 75.0M ) { filteredInfos.Add( String.Format( "{0} - €{1}", product.ProductName, product.UnitPrice ) ); } } } Dieses Codebeispiel ist als eine Sequenz von Befehlen im imperativen Stil geschrieben. Es wird eine neue Liste erzeugt, danach über alle bestehenden Produkte iteriert und an- schließend werden die Produkte, die einen bestimmten Preis übersteigen, in eine neue Liste eingefügt. Beschreiben wir das Problem etwas abstrakter, so wird klar, dass ledig- lich eine Auflistung von Produkten gefiltert werden soll.
8 Grundlagen der funktionalen Programmierung Mit C#, ab der Version 3.0, kann dieses Problem auch mit Hilfe von LINQ gelöst wer- den. Das folgende Codebeispiel beschreibt wesentlich anschaulicher und lesbarer, wel- ches eigentliche Ziel erreicht werden soll. Deklarative Datenverarbeitung mit LINQ (C#) public IEnumerable GetExpensiveProducts() { return from product in this.Products where product.UnitPrice > 75.0M select String.Format( "{0} - €{1}", product.ProductName, product.UnitPrice ); } Dieser einzelne zusammenhängende Ausdruck besteht aus elementaren Operatoren wie z.B. select und where. Diese Operatoren nehmen wiederum andere Ausdrücke als Argumente, um zu ermitteln was gefiltert werden soll und welches Ergebnis zurückge- geben werden soll. Diese Operatoren geben dem Entwickler also eine Möglichkeit, ver- schiedene Teile des Codes zu kombinieren, ohne dabei mehr Code schreiben zu müssen. Ein weiterer interessanter Aspekt ist, dass dieser Ausdruck wesentlich einfacher zu le- sen ist, da die konkrete Implementierung der elementaren Operatoren vor dem Entwick- ler versteckt wird. Es wurde eine Lösung geschaffen, die einerseits einfacher von der Implementierung, als auch flexibler in der Anwendung ist. In Kapitel 6.1 wird außer- dem ersichtlich, dass es so einfacher ist, Code zu parallelisieren. 2.3.2 Beschreibung einer Benutzeroberfläche mit XAML Die Gestaltung von Benutzeroberflächen hat sich in den vergangenen Jahren gerade durch den Einsatz von Designern innerhalb den Entwicklungsumgebungen stark verbes- sert. Code wird immer häufiger von Generatoren automatisiert erzeugt, da dieser, gerade bei komplexen Oberflächen, für Entwickler kaum noch überschaubar ist. Die Windows Presentation Foundation (WPF) und ihre enthaltene deklarative Sprache XAML gehen dabei einen Schritt weiter. Seit der Version 3.0 sind beide ein fester Be- standteil des .NET Frameworks und bringen neben dem Compiler auch einen Laufzeit- parser für XAML mit. XAML ist eine allgemein einsetzbare deklarative Sprache und besonders geeignet um Benutzeroberflächen zu gestalten. Sie ermöglicht die Trennung zwischen dem Teil, der die Ansicht der Oberfläche beschreibt und dem Teil, der die konkrete Programmlogik
9 Grundlagen der funktionalen Programmierung enthält. Genau wie bei XML wird die Beschreibung der Oberfläche in einer Baumstruk- tur repräsentiert. Dadurch werden eine wesentlich schlankere Form der Oberfläche so- wie eine bessere Lesbarkeit gewährleistet. Einen Designer gibt es nach wie vor, jedoch sind Anpassungen an automatisch generierten Code einfacher möglich. Erzeugung eines UI im deklarativen Stil (XAML) Der XAML Code beschreibt die Benutzeroberfläche durch den Einsatz elementarer Schlüsselworte und das Festlegen von bestimmten Eigenschaften, so dass der gesamte Code zu einem einzigen Ausdruck wird. Auch hier wird wieder sehr deutlich, dass le- diglich festgelegt wird was das Ergebnis sein soll und das wie in einer Blackbox vor den Entwicklern versteckt wird. 2.3.3 Komposition Es wurde bereits gezeigt, dass es möglich ist, viele Probleme durch den deklarativen Stil einfacher zu lösen, wenn zu diesem Zweck zuvor die nötigen Bibliotheken erstellt wur- den. Datenstrukturen können mit Hilfe von LINQ einfach abgefragt und Benutzerober- flächen mit XAML leicht verständlich erstellt werden. Für die spezifischeren Probleme, die einen eigenen Bereich betreffen, gibt es jedoch meist keine vorgefertigten Bibliotheken. Die Funktionalität, die vor den Entwicklern in einer Blackbox versteckt wird, entsteht jedoch nicht auf magische Weise, sondern muss zunächst einmal implementiert und in einer Bibliothek gekapselt werden. Im Folgenden wird beispielhaft gezeigt wie solch eine Bibliothek entstehen könnte. Erstellen einer funktionalen Animation (C#) var green = Anims.Circle(Brushes.OliveDrab, 100.0f.Anim()); var blue = Anims.Circle(Brushes.SteelBlue, 100.0f.Anim()); var animatedPos = Time.Wiggle * 100.0f.Anim(); var greenMove = green.MoveXY(animatedPos, 0.0f.Const()); var blueMove = blue.MoveXY(0.0f.Const(), animatedPos); var animation = Anims.Compose(greenMove, blueMove);
10 Grundlagen der funktionalen Programmierung Es ist nicht wichtig das Beispiel im Detail zu verstehen. Im Grunde genommen werden lediglich 2 Kreise erzeugt, eine Bewegung mit Hilfe der MoveXY Methode erzeugt und durch die Compose Methode in eine einzelne Animation gekapselt. Ein wichtiger Aspekt von deklarativen Bibliotheken ist die Tatsache, dass sie kombi- nierbar eingesetzt werden können. In diesem Beispiel ist eine solche Komposition z.B. durch das Erstellen einer einzelnen Animation aus mehreren einzelnen Bewegungen deutlich geworden. Ein anderes Beispiel wäre LINQ, wo komplexe Abfragen aus meh- reren einzelnen kleineren Abfragen erstellt werden können. Somit könnten z.B. auch eigene Primitive erzeugt werden, um damit Animationen zu erstellen.
11 Kernkonzepte von F# 3 Kernkonzepte von F# Der Fokus in diesem Kapitel liegt darauf, die Kernkonzepte der Sprache F# kennenzu- lernen und einen Einblick in allgemeine Ideen sowie elementare Techniken der funktio- nalen Programmierung zu bekommen. 3.1 Auswertung funktionaler Programme Im vorherigen Kapitel wurde bereits angedeutet, dass funktionale Programme unverän- derliche Datenstrukturen nutzen. Man mag sich nun fragen, wie ein Programm über- haupt etwas tun kann, wenn alles unveränderlich ist. Die Antwort ist kurz wie auch ein- fach. Ein funktionales Programm wird nicht als eine Verkettung von Ausdrücken be- schrieben die den Zustand verändern sondern mehr als eine systematische Anwendung von Regeln.4 3.1.1 Verwenden von unveränderlichen Datenstrukturen Wird innerhalb eines funktionalen Programmes von Datenstrukturen gesprochen, so sind im allgemeinen Werttypen oder Klassen, wie sie aus C# bekannt sind, gemeint. Diese Datenstrukturen sind in F# unveränderlich, sobald sie zum ersten Mal initialisiert wurden. In C# können einzelne Daten mit Hilfe des Schlüsselwortes readonly eben- falls als unveränderlich gekennzeichnet werden. Ebenso ist es auch möglich in F# ver- änderliche Typen zu erzeugen, jedoch ist der Standard, dass Typen nicht veränderlich sind. Das bedeutet auch, dass Methodenaufrufe nicht den Zustand einer Struktur verän- dern, sondern stets einen Rückgabewert besitzen der weiter verarbeitet werden muss. Das kann bei der Initialisierung einer Liste z.B. wie folgt aussehen. Erstellen einer Liste var list = ImmutableList.Empty().Add(1).Add(3).Add(5).Add(7); Dieses Beispiel ist jedoch extrem simpel und sobald darüber nachgedacht wird ein komplexeres Problem auf dieselbe Weise anzugehen, kann es schwierig werden. 4 Vgl. computation by calculation (Hudak, 2000)
12 Kernkonzepte von F# 3.1.2 Den Zustand eines Programms durch Rekursion verändern Um aufzuzeigen wie komplexere Probleme gelöst werden können, wird im Folgenden eine Funktion implementiert, die die Zahlen x bis y aufsummiert, ohne dabei auf lokale Variablen zurückzugreifen. Im einfachsten Sinne und durch Verwendung lokaler Vari- ablen könnte diese Funktion wie folgt aussehen: Aufsummieren von Zahlen mit lokaler Variable (C#) int SumNumbers( int from, int to ) { int res = 0; for ( int i = from; i to ) return 0; int sumRest = SumNumbers( from + 1, to ); return from + sumRest; } Dieses Beispiel ist funktional, da es lediglich mit Wertzuweisungen und Rückgabewer- ten arbeitet. Die Rekursion wird hier durch einen Selbstaufruf und eine Abbruchbedin- gung innerhalb der Funktion repräsentiert. Der Zustand der Berechnung wird nun nicht mehr in einer lokalen Variablen festgehalten, sondern wird durch die Rekursion selbst ausgedrückt. Den rekursiven Teil einer Berechnung jedes Mal selbst zu schreiben wäre jedoch aufwändig und auch hier gibt es wiederum einen Weg den schwierigen Teil zu verstecken und Probleme zu lösen ohne explizit auf Rekursionen zurückzugreifen. 5 Eine Technik, in der eine Funktion sich selbst über den Aufruf von selbst definiert
13 Kernkonzepte von F# 3.1.3 Verwenden von Ausdrücken an Stelle von Anweisungen Bei der imperativen Programmierung ist ein Ausdruck ein einfaches Stück Code, wel- ches ausgewertet wird und dann ein Ergebnis liefert. So ist z.B. ein Methodenaufruf bzw. die Verwendung eines booleschen- oder Zahlenoperators ein Ausdruck. Eine An- weisung hingegen ist ein Stück Code welches den Zustand des Programms verändert. Eine Anweisung wäre somit z.B. der Aufruf einer Methode, die keinen Rückgabewert besitzt oder die Zuweisung eine Wertzuweisung zu einer Variablen. Bei funktionalen Sprachen wird der Zustand durch das Ergebnis einer Funktion reprä- sentiert und weitergegeben. Wird diese Logik weiter verfolgt, so lässt sich sagen, dass in der funktionalen Sprache alles als Ausdruck bezeichnet werden kann. Betrachtet man nochmals das Beispiel aus dem vorherigen Abschnitt, in welchem Zahlen aufaddiert wurden, so lässt sich feststellen, dass es nicht vollkommen funktional ist, da es in einer Folge von drei Anweisungen geschrieben ist. Mit Hilfe einer kleinen Anpassung lässt sich dieses Beispiel jedoch auf eine vollständig funktionale Version verbessern. Aufsummieren von Zahlen durch Rekursion (C#) int SumNumbers( int from, int to ) { return ( from > to ) ? 0 : { int sumRest = SumNumbers( from + 1, to ); from + sumRest; } } Der Methodenkörper ist jetzt rein funktional, da er nur noch aus einem einzelnen Aus- druck besteht. Dies ist in der Sprache C# nur in relativ seltenen Fällen möglich, da in- nerhalb von konditionellen Auswertungen keine lokalen Variablen verwendet werden können. Obwohl dieses Beispiel sehr minimalistisch gehalten ist, gibt es einen Hinweis darauf, was man in der funktionalen Sprache alles schreiben kann: Der Methodenkörper ist ein einzelner Ausdruck. In C# bedeutet das auch, dass der Körper mit dem Schlüsselwort return beginnt. Da If-Then-Else ebenfalls eine Anweisung ist, muss man auf den konditionel- len Operator (?:) zurückgreifen. Der Ausdruck im else Block enthält eine Variablendeklaration gefolgt von ei- nem Ausdruck. Es wird eine lokale Variable sumRest deklariert welche im rest- lichen Codeblock verfügbar bleibt. Variablendeklarationen in F# funktionieren auf dieselbe Art und Weise. Die Deklaration ist zwar kein Ausdruck aber ein
14 Kernkonzepte von F# syntaktisches Hilfskonstrukt welches an einen Ausdruck angehangen werden kann. 3.2 Schreiben von deklarativem Code Bisher wurde lediglich beleuchtet, welche Auswirkungen die deklarative Programmie- rung hat und in wie weit die Anwendung dieses Programmierstils das Entwickeln er- leichtern kann. Die folgenden Kapitel sollen zeigen, wie dies auf der technischen Seite überhaupt möglich gemacht wurde. Aus der technischen Sicht deuten zwei Aspekte besonders auf den deklarativen Stil hin. Der Erste von beiden wurde zuletzt behandelt – nämlich das jedes Sprachkonstrukt ein Ausdruck ist. Es wird kein genaues Wissen mehr über einzelne Anweisungen benötigt, da sich ein Programm aus Ausdrücken zusammensetzt die miteinander kombiniert wer- den. Der Zweite Aspekte beschäftigt sich mit dem Problem, dass Funktionen für unter- schiedliche Zwecke eingesetzt werden können. 3.2.1 Funktionen als Argumente Wird der zweite Aspekt aus Kapitel 3.2 betrachtet, so stellt kommt folgende Fragen zustanden: „Wie lässt sich der variable Teil einer Methode von dem immer gleichblei- benden Teil separieren?“. Die Antwort auf diese Frage ist relativ einfach: „Der gleich- bleibende Teil bildet den Körper der Methode und die Parameter bilden den variablen Teil, den die Methode ausführen soll“. Eine Modifikation, an dem Beispiel SumNumbers aus Kapitel 3.1.2, könnte so aussehen, dass diese die Funktionalität erhält sämtliche Aggregationen durchzuführen, anstatt le- diglich Zahlen aufzuaddieren. Folgendes Beispiel demonstriert dies: Aggregieren von Zahlen durch Rekursion (C#) public int AggregateNumbers( Func op, int init, int from, int to ) { if ( from > to ) return init; int sumRest = AggregateNumbers( op, init, from + 1, to ); return op( from, sumRest ); } Zur ursprünglichen Funktion sind zwei weitere Parameter hinzugekommen – der Initiale Wert (init) und eine Operation die beschreibt wie die kommenden Zahlenwerte trans-
15 Kernkonzepte von F# formiert werden sollen. Die Operation wird in Form eines delegates übergeben, wel- cher spezifiziert, dass er eine Funktion mit zwei Parametern vom Typ int ist und auch den Typ int als Rückgabewert besitzt. Die Idee Funktionen als Argumente zu nutzen ist eines der nützlichsten funktionalen Konzepte. Wird angenommen, dass eine bestehenden Kundenliste sortiert werden soll, so wäre der klassische objektorientierte Lösungsweg die Sort Funktion einer Liste zu nutzen und eine eigene Sortierklasse, die das IComparer Interface implementiert, mit zu übergeben. Diese Klasse würde bestimmen wie zwei Elemente miteinander verglichen werden können. Ein nicht ganz unerheblicher Aufwand, wenn bedacht wird, dass eine Klasse für diesen Vorgang geschrieben, diese zum Einsatz instanziiert und abschließend als Argument übergeben werden müsste. Ganz davon abgesehen, dass die Lesbarkeit des eigentlichen Zieles nicht deutlich zu erkennen ist. 3.2.2 Funktionen höherer Ordnung Inzwischen wurde gezeigt, dass Funktionen wie Werte gehandhabt werden können und sogar als Parameter für andere Funktionen dienen können. Es gibt zwei wichtige Begrif- fe die oft genutzt werden, wenn über diese Art von Funktionen gesprochen wird: Funktion erster Klasse (First-class function) ist eine Funktion in Form eines Wertes. Man kann diese also beispielsweise als Argument für eine andere Funk- tion weiter nutzen. Als Ergebnis haben diese Funktionen auch einen Typ, der in C# als delegate ausgedrückt wird. Beim Aufruf der Funktion greift diese auf ihren Erstellungskontext zurück. 6 Funktion höher Ordnung (Higher-order function) bezieht sich auf eine Funkti- on, die wiederum eine Funktion als Argument nimmt oder als Rückgabewert be- sitzt. Das Beispiel aus Kapitel 3.2.1 ist beispielsweise eine Funktion höherer Ordnung. Diese Art der Parametrisierung wird in funktionalen Sprachen sehr häufig eingesetzt und hilft dabei Quellcode deklarativer zu gestalten. 7 Mit einem weiteren Beispiel in der Sprache F# sollen diese Aspekte verdeutlicht wer- den. Zunächst werden aus einer Liste die ungeraden Zahlen herausgefiltert und an- schließend das Quadrat der jeweiligen Zahlen gebildet. 6 Vgl. (Microsoft, 2012) 7 Vgl. (Lorenz, 2009)
16 Kernkonzepte von F# Verarbeiten einer Zahlenliste mit Funktionen höherer Ordnung (F#) > let numbers = [1..10] let isOrdd(n) = n % 2 = 1 let square(n) = n * n ;; val numbers : int list val isOdd : int -> bool val square : int -> int > List.filter isOdd number;; val it : int list = [1; 3; 5; 7; 9] > List.map square (List.filter isOdd numbers);; val it : int list = [1; 9; 25; 49; 81] Nachdem eine Liste mit den Zahlen von 1 bis 10 erzeugt wurde, werden zwei weitere Funktionen gestellt. Die Erste Funktion gibt zurück, ob der übergebene Wert (n) durch zwei teilbar und somit gerade oder ungerade ist. Die zweite Funktion bildet aus dem übergebenen Wert das Quadrat. Nach der Definition der Funktionen, wird eine Funktion höherer Ordnung, List.filter, genutzt, die wiederum als erstes Argument eine Funktion und als zweites Argument eine Liste einfordert. Wie anhand der folgenden Zeile zu erkennen ist, wird als Rückgabewert eine Liste mit ungeraden Zahlen geliefert. Das darauffolgende Beispiel gibt noch eine weitgehendere Idee. Es wird die Funktion List.map aufgerufen und als erstes Argument die zuvor geschriebene Funktion square übergeben. Als zweites Argument wird aber nicht etwa eine gewöhnliche vorgefilterte Liste übergeben, sondern wiederum eine Funktion die als Ergebnis eine Liste als Rück- gabewert besitzt, nämlich selbige Funktion aus dem ersten Beispiel. 3.3 Typen in der funktionalen Programmierung Da funktionale Sprachen jedes Stück Code als Ausdruck werten, ist es eine eher gewag- te Aussage zu sagen, dass jeder Ausdruck einen Typ besitzt. Es bedeutet, dass jedes syntaktisch korrekte Stück Code einen bestimmten Typ hat. Dieser sagt aus was für eine Art von Ergebnis erwartet wird sobald ein Ausdruck ausgewertet wird und gibt somit eine wertvolle Information über einen Ausdruck. Typen können als eine Art grammatikalische Regeln für die Zusammensetzung von primitiven Ausdrücken gesehen werden. In funktionalen Sprachen haben Funktionen
17 Kernkonzepte von F# einen Typ, genauso wie die square Funktion aus dem vorherigen Beispiel. Dieser Typ bestimmt auch gleichzeitig wie eine Funktion verwendet werden kann. Wird sie mit einem Argument vom Typ int aufgerufen, so lautet der Typ des Rückgabewertes auch int. Wichtiger jedoch ist jedoch die Tatsache, dass der Typ auch vorgibt in wie weit eine Funktion mit einer anderen Funktion genutzt werden kann. So kann die Funktion squa- re nicht mit der Funktion List.filter verwendet werden, da die Filterung eine Funk- tion mit dem Typ boolean erwartet. Genau das beschreibt auch eine grammatikalische Regel – die Typen stellen sicher, dass Funktionen stets in einem sinnvollen Kontext verwendet werden.
18 Anwendungskonzept Pixxler 4 Anwendungskonzept Pixxler Um die Kombination von funktionalen und objektorientierten Sprachelementen mög- lichst praxisnah zu erproben, wurde im Rahmen dieser Bachelorarbeit ein Konzept für eine Anwendung erstellt, welche für den Rest dieser Arbeit den Arbeitstitel „Pixxler“ trägt. Die Idee dahinter ist eine multiuserfähige Anwendung die Bilder durch verschie- dene Farb- und Bildfilter nachbearbeitet und in der Cloud speichert. Die Bilder werden dabei in zuvor, vom Benutzer, angelegten Alben gespeichert. Da die zustande kommen- den Daten jedoch nicht lokal beim Benutzer gespeichert werden sollen, sondern in einer zentralen Datenbank, korrespondiert die Anwendung mit einem Service innerhalb eines Netzwerks. Wichtig bei der Konzeption des Projektes war auch der Gedanke der Erwei- terbarkeit. Eine absolute Erweiterbarkeit kann allerdings nicht garantiert werden, denn diese ist für einen Entwickler immer mit zusätzlichem Aufwand und erhöhter Komple- xität des Programms verbunden. Bei der Abwägung einen Service als Schnittstelle für den Datenverkehr und andere Dienstleistungen einzusetzen überwogen die Vorteile je- doch dem Aufwand, besonders in Hinsicht der Multiuserfähigkeit und Erweiterbarkeit der gesamten Plattform. Weiterhin soll auch die Praxistauglichkeit sowie der moderne Ansatz der serviceorientierten Architektur in Hinsicht auf funktionale Eigenschaften näher untersucht werden. 4.1 Serviceorientierte Architektur Die Idee, die hinter diesem Anwendungskonzept steckt, nennt sich serviceorientierte Architektur (SOA). Erreicht wird dadurch, dass ein oder gar mehrere unabhängige verteilte Services einen Bestandteil eines Programmes übernehmen. Ei- ne pragmatische Standardisierung von Schnitt- stellen sorgt, in Verbindung mit XML als ein- heitlichem Datenformat, für die notwendige Abbildung 1: SOA (Crofton, 2010) Interoperabilität. Dadurch wird außerdem ge- währleistet, dass unterschiedliche Endanwendungen mit Hilfe dieser externen Schnitt- stellen auf gleiche Weise funktionieren.
19 Anwendungskonzept Pixxler 4.2 WCF Service Pixxler Das Speichern von allen gesammelten Daten in der Cloud ist ein zentraler Aspekt in diesem Projekt. Alle, von Clients erzeugten Daten, sollen in einer zentralen Datenbank gespeichert werden. Um die Datenbank selbst jedoch nach außen hin abzusichern und eine komfortable und neutrale Schnittstelle für Clientanwendungen zu gewährleisten wird ein Service mit Hilfe der Windows Communication Foundation (WCF) entwickelt. Die auf Nachrichten basierte Kommunikation zwischen den Klienten und dem Service wird dabei über das Simple Object Access Protokoll (SOAP) realisiert. Diese Nachrich- ten werden verschlüsselt über einen Datenstrom versendet und sind somit gegenüber Dritten weitestgehend geschützt. Um gespeicherte Daten aus der Datenbank abzufragen wird auf einen Objektrelationalen Mapper zurückgegriffen. Dieser Mapper erleichtert die Ansteuerung der Datenbank aus dem Quellcode des Services und ermöglicht den Einsatz der funktional orientierten .NET Erweiterung LINQ to Entities. Durch den Ein- satz von LINQ soll außerdem gezeigt werden, welche Vorteile der funktionale Ansatz auch im Bereich der Abfrage von Daten aus einer Datenbank haben kann. Der Service dient somit insgesamt sowohl als Vermittlungsstelle zwischen Anwendungen und Da- tenbank, als auch als Dienstleister für andere Aufgaben wie die Authentifizierung von Benutzern. 4.3 Pixxler API Eine API soll den Zugriff auf den Service als weitere Schicht abstrahieren damit die Ansteuerung dessen nicht direkt im Quellcode der jeweiligen Endanwendung imple- mentiert werden muss. Diese Bibliothek enthält alle vom Service angebotenen Schnitt- stellenmethoden, eine Struktur zur lokalen Datenhaltung sowie eine lokale Implementie- rung des Authentifizierungsmechanismus. Die Verwendung einer solchen Bibliothek hat den Vorteil, dass die Ansteuerung des Services auch von anderen Endanwendungen auf gleiche Weise wiederverwendet werden kann und nicht im Quellcode mehrerer Endanwendungen erneut implementiert werden muss. Es kann im späteren Verlauf so- gar in Betracht gezogen werden, diese API öffentlich zugänglich zu machen und Dritt- anbietern von Software einen Zugriff auf den Service zu gewähren. Die API wird als eigenständiges Modul in die Endanwendung eingebunden.
20 Anwendungskonzept Pixxler 4.4 Clientanwendung Pixxler Da auch die Vorteile der deklarativen Programmierung näher vorgestellt werden sollen, wird die Benutzeroberfläche der Clientanwendung mit der, XML ähnlichen, deklarati- ven Sprache XAML gestaltet. Sie ist fester Bestandteil der Windows Presentation Foundation (WPF) und wird eingesetzt um grafische Elemente, Benutzeroberflächen, Verhaltensweisen, Animationen, Transformationen, Darstellung von Farbverläufen und weitere mediale Inhalte zu definieren. Die möglichst hohe Flexibilität der Anwendung wird durch eine modulare Architektur gewährleistet. Die Modularisierung der Endanwendung wird dabei durch den Einsatz der .NET Bibliothek PRISM, sowie den modernen Ansatz der Dependency Injection möglich gemacht, um auch in der Zukunft eine leichte Erweiterbarkeit möglich zu ma- chen. Sogenannte Softwaremodule bilden dabei die eigentliche Funktionalität der End- anwendung. Folgende Module sind für die Anwendung vorgesehen: Modul für den Login Modul zur Navigation der Bilderalben und deren Inhaltsanzeige Modul zur Anzeige von Bildern und deren Bearbeitung Bei der Entwicklung der Clientanwendung wird C# als Sprache für die Anwendungsar- chitektur sowie alle anderen objektorientierten Bereiche verwendet. 4.5 F# Bibliothek Die Anforderung ist, dass der mit F# implementierte funktional orientierte Teil als Bib- liothek in der Clientanwendung bzw. im Service ansprechbar sein sollte. In dieser Bibli- othek sollen sämtliche Funktionalitäten bereitgestellt werden, um Bilder mit verschie- denen Farb- und Effektfiltern neu berechnen zu lassen. Da der F# Code beim Kompilie- ren, genau wie C# Code, in den Zwischencode für die CLR8 umgewandelt wird, ist eine Verwendung dieser Bibliothek aus einer .NET Anwendung bzw. dem Service problem- los möglich. Voraussetzung dafür ist allerdings die Einbindung der allgemeinen F# Bib- liothek aus dem .NET Framework in das jeweilige Projekt. 8 Dies ist die Laufzeitumgebung von .NET
21 Implementierung des Pixxler Services 5 Implementierung des Pixxler Services Der Pixxler Service stellt eine zentrale Anlaufstelle für ausgelagerte Aufgaben aller Art dar. Er ist ein elementarer Baustein für sämtliche Endanwendungen und muss somit auch als erstes konzipiert und implementiert werden. Folgende Aufgaben soll der Ser- vice in der ersten Version übernehmen: Authentifizierung der Benutzer Vermittlungsstelle für Daten aus der Datenbank Da es sich um eine multiuserfähige Anwendung handeln soll, wird eine Authentifizie- rung am Service notwendig. Die Abfrage benutzerbezogener Daten muss dabei inner- halb einer Sitzung ablaufen um sicher zu stellen, dass jedem Benutzer die passenden Daten zur Verfügung gestellt werden können. Nach der erfolgreichen Anmeldung soll dem Benutzer eine SessionID 9 vergeben werden, mit welcher dieser dann weitere Leis- tungen des Services nutzen kann. Die SessionID wird in Form einer GUID repräsentiert um die weltweite Einmaligkeit der Sitzung zu gewährleisten. Bei den Leistungen des Services handelt es sich sowohl um die Abfrage von Bildern und Alben die einem Benutzer zugeordnet sind als auch um Routinen, die zum Spei- chern neuer Bilder und Alben dienen. Um die Ansteuerung des Services nicht für jedes unterschiedliche Endgerät neu zu ent- wickeln und ggf. sogar später eine öffentliche Ansteuerung zu ermöglichen, wird für den Service eine API in Form einer .NET Bibliothek entwickelt um die Datenstrukturen, Konnektivität und das Handhaben der Sitzung für die Anwendungsentwicklung bereit- zustellen. 5.1 Was ist ein WCF Service Die Windows Communication Foundation (WCF) ist eine von Microsoft entwickelte Technologie, die es Entwicklern ermöglicht Kommunikationsplattformen zu erstellen die mit standardisierten Protokollen (TCP, HTTP) arbeiten. Die WCF ist somit beson- ders dazu geeignet um serviceorientierte Anwendungen zu entwickeln. 9 Eine eindeutige alphanumerische Zeichenfolge die dazu dient den Klienten wiederzuerkennen
22 Implementierung des Pixxler Services Ein WCF Dienst ist dabei in folgende Bereiche unterteilt: Eine Adresse die den Ort der Erreichbarkeit in Form einer URI beschreibt Die Bindung, welche die Form der Kommunikation sowie eingesetzte Kommu- nikationsprotokoll beschreibt. Verschlüsselung fällt ebenfalls in diesen Bereich. Ein Vertrag, der beschreibt welche Dienstleistungen in Form von Methoden der Service anbietet. Abbildung 2: WCF - ABC Prinzip 10 Dieses ABC (Address, Binding, Contract) Prinzip abstrahiert somit das Konzept des Endpunktes. 5.2 Einrichten der Datenbank Die Daten der Benutzer müssen in einer Datenbank persistent gespeichert werden. Als Speicherort für diese Daten dient ein Microsoft SQL Server 2008 R2 mit einer Stan- darddatenbank. Die zu speichernden Daten sind zu Anfang sehr überschaubar und wer- den auf folgende Strukturen projiziert: Benutzer Alben und ihre Zugehörigkeit zum Benutzer Zugriffsrechte auf Alben anderer Benutzer Bilder und ihre Zugehörigkeit zum Album Da die Anwendung darauf ausgelegt ist von mehreren Benutzer genutzt zu werden, muss die Tabellenstruktur in der Datenbank ebenfalls benutzerbezogen gestaltet werden. Jeder Benutzer soll X Alben anlegen können und in diesen alle gewünschten Bilder an- ordnen können. Es soll außerdem berücksichtigt werden, dass in einer späteren Version 10 (Beaulieu, 2010)
Sie können auch lesen