Foundations for Professionals .NET Professionals im Profil guide to C# guidgen.de

Blog

Nutze den Augenblick
und teile der Welt mit, was Du zu sagen hast.

guide to C# Live! 2009

Montag, 30. März 2009, 22:04 Uhr
Permalink | Kommentare (0) | Kommentare als RSSRSS

Vom 30. Mai bis zum 1. Juni 2009 findet wieder guide to C# Live! statt – dieses Mal mit dem Fokus auf dem Thema Softwarequalität.

Die Veranstaltung findet in Pforzheim statt, die Teilnahme ist auf 25 Personen begrenzt. Dabei kostet die Teilnahme regulär 360 Euro, beziehungsweise 60 Euro für Schüler und Studenten, sofern ein entsprechender gültiger Nachweis erbracht wird, jeweils zuzüglich der Kosten für An- und Abreise, Übernachtung und Verpflegung.

Nachdem guide to C# Live! im vergangenen Jahr zwei Mal von Golo Roden alleine durchgeführt wurde, sind dieses Mal drei Referenten vertreten, die durch ihr Engagement in der Community bekannt sind:

Seit Januar 2009 bieten sie zudem unter ihrer gemeinsamen Marke architeXis qualitativ hochwertiges Training und Consulting zu C#, .NET und verwandten Technologien wie beispielsweise SQL Server und IIS an.

Nähere Informationen sowie die Möglichkeit zur Anmeldung finden sich ab sofort auf der Webseite von guide to C# Live!.

Auf Application.Start in einem HttpModule reagieren

Freitag, 20. März 2009, 17:58 Uhr
Permalink | Kommentare (3) | Kommentare als RSSRSS

In ASP.NET ist es mit Hilfe der Datei Global.asax möglich, auf anwendungsweite Ereignisse zu reagieren. Dazu zählen unter anderem:

  • Start der Anwendung
  • Start einer Session
  • Ende einer Session
  • Ende der Anwendung

Allerdings handelt es sich bei diesen Ereignissen um keine Ereignisse im eigentlichen Sinne, wie man sie ansonsten aus C# kennt. Statt dessen ist der Aufruf der jeweiligen ereignisbehandelnden Methode in der Global.asax in der Engine von ASP.NET hart verdrahtet.

Während es bei anderen Ereignissen, wie beispielsweise dem Start eines Requests, problemlos möglich ist, die Behandlung in ein HttpModule zu verschieben, ist es mit diesen vier Ereignissen also nicht möglich – es gibt schlichtweg kein Ereignis, an das man das HttpModule anhängen könnte.

Doch zumindest für Application.Start gibt es einen Workaround: Es genügt, einen leeres HttpModule zu erstellen, das keinerlei Funktionalität enthält, und dieses mit einem statischen Konstruktur auszustatten:

using System.Web;

namespace silkveil.net.Application
{
    /// <summary>
    /// Represents the application module that starts up the application.
    /// </summary>
    public class ApplicationModule : IHttpModule
    {
        /// <summary>
        /// Initializes the <see cref="ApplicationModule" /> type.
        /// </summary>
        /// <remarks>
        /// A static constructor is used since HTTP modules are not able to subscribe to the start
        /// event of an ASP.NET application. This is only possible in the Global.asax file. So, a
        /// basically useless module is used to have a static constructor available (which gets
        /// executed only once).
        /// </remarks>
        static ApplicationModule()
        {
            // TODO: Insert your initialization code here ...
        }

        /// <summary>
        /// Initializes a module and prepares it to handle requests.
        /// </summary>
        /// <param name="context">
        /// An <see cref="T:System.Web.HttpApplication" /> that provides access to the methods,
        /// properties, and events common to all application objects within an ASP.NET application.
        /// </param>
        public void Init(HttpApplication context)
        {
            // At the moment, nothing needs to be done here.
        }

        /// <summary>
        /// Disposes of the resources (other than memory) used by the module that implements 
        /// <see cref="T:System.Web.IHttpModule" />.
        /// </summary>
        public void Dispose()
        {
            // At the moment, nothing needs to be done here.
        }
    }
}

Da dieses HttpModule bei jedem Request aufgerufen wird, wird es auch beim ersten Request – also quasi direkt nach dem Start der Anwendung – aufgerufen. Zuvor wird jedoch der statische Konstruktor ausgeführt, so dass der Code zur Initialisierung der Anwendung hier platziert werden kann.

Da die CLR garantiert, dass ein statischer Konstruktor nur ein Mal pro Typ ausgeführt wird, lässt sich auf diese Art das gewünschte Verhalten mit wenig Aufwand nachbilden.

WCF ohne .svc

Donnerstag, 19. März 2009, 19:21 Uhr
Permalink | Kommentare (2) | Kommentare als RSSRSS

Um in .NET einen WCF-Dienst auszuführen, muss prinzipiell lediglich eine Instanz der Klasse ServiceHost erzeugt werden, die anschließend das Hosting für den jeweiligen Dienst übernimmt.

Nahezu jedes Buch zu WCF enthält dementsprechend ein Beispiel, wie man einen einfachen Host für WCF-Dienste auf Basis der Konsole entwickelt. Auf Grund gewisser Anforderungen wie Verfügbarkeit, Skalierbarkeit und ähnlichem genügt ein solcher Host in der Praxis jedoch in den seltensten Fällen.

Da .NET erst ab der Version 4.0 über einen Application Server verfügen wird, stellt derzeit der IIS die beste Alternative dar, da dieser neben Webseiten auch WCF-Dienste hosten kann. Allerdings benötigt der IIS hierfür in jedem Fall eine .svc-Datei, die auf den eigentlichen Dienst verweist:

<% @ServiceHost language="C#" Debug="true" Service="MyService" CodeBehind="~/App_Code/MyService.cs" %>

Sofern man Wert auf ein einfaches Deployment legt und den WCF-Dienst in einer einzigen DLL bereitstellen will, die sämtlichen Code enthält, stößt man nun auf ein Problem: Es ist scheinbar unter WCF nicht möglich, die .svc-Datei ebenfalls in die DLL auszulagern – es müssen also immer zwei Dateien angefasst werden.

Doch es gibt eine Lösung für dieses Problem: ASP.NET stellt im Namensraum System.Web.Hosting die Klasse VirtualPathProvider zur Verfügung, mit deren Hilfe ein virtuelles Dateisystem implementiert werden kann.

Um also einen WCF-Dienst ohne .svc-Datei im IIS zu hosten, muss ein eigener VirtualPathProvider implementiert werden, der auf Anfragen nach der Datei Service.svc mit einer per Code erzeugten Variante reagiert.

Damit ein VirtualPathProvider funktioniert, müssen die beiden Methoden FileExists und GetFile überschrieben werden, die zudem prüfen müssen, ob der aktuelle Provider überhaupt zuständig ist – falls nicht, wird die Ausführung an den nächsten in ASP.NET registrierten Provider übergeben:

using System.Web.Hosting;
using System.Web;

namespace silkveil.net.Wcf
{
    /// <summary>
    /// Provides activation for a WCF service without the need for a .svc file.
    /// </summary>
    public class ServiceActivationProvider : VirtualPathProvider
    {
        /// <summary>
        /// Checks whether the specified path is a virtual one.
        /// </summary>
        /// <param name="virtualPath">The virtual path.</param>
        /// <returns><c>true</c> if the path is virtual; otherwise <c>false</c>.</returns>
        private static bool IsVirtualPath(string virtualPath)
        {
            // Check whether the path is virtual.
            virtualPath = VirtualPathUtility.ToAppRelative(virtualPath);
            return virtualPath == "~/Service.svc";
        }

        /// <summary>
        /// Gets a value that indicates whether a file exists in the virtual file system.
        /// </summary>
        /// <returns>
        /// <c>true</c> if the file exists in the virtual file system; otherwise, <c>false</c>.
        /// </returns>
        /// <param name="virtualPath">The path to the virtual file.</param>
        public override bool FileExists(string virtualPath)
        {
            // If the path is not virtual, delegate to the previous provider in the provider chain.
            if(!IsVirtualPath(virtualPath))
            {
                return this.Previous.FileExists(virtualPath);
            }

            // Otherwise, always return true.
            return true;
        }

        /// <summary>
        /// Gets a virtual file from the virtual file system.
        /// </summary>
        /// <returns>
        /// A descendent of the <see cref="T:System.Web.Hosting.VirtualFile" /> class that
        /// represents a file in the virtual file system.                
        /// </returns>
        /// <param name="virtualPath">The path to the virtual file.</param>
        public override VirtualFile GetFile(string virtualPath)
        {
            // If the file does not exist in this virtual path provider, delegate execution to
            // the previous provider in the provider chain.
            if (!IsVirtualPath(virtualPath))
            {
                return this.Previous.GetFile(virtualPath);
            }

            // Create a Service.svc file on the fly and return it to the caller.
            return new ServiceActivationFile(virtualPath);
        }
    }
}

Außer diesem Provider muss zusätzlich auch noch eine Klasse von VirtualFile abgeleitet werden, welche die eigentliche .svc-Datei erzeugt:

using System.IO;
using System.Web.Hosting;

namespace silkveil.net.Wcf
{
    /// <summary>
    /// Represents a virtual .svc file.
    /// </summary>
    public class ServiceActivationFile : VirtualFile
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceActivationFile" /> type.
        /// </summary>
        /// <param name="virtualPath">The virtual path.</param>
        public ServiceActivationFile(string virtualPath) : base(virtualPath)
        {
        }

        /// <summary>
        /// Returns a read-only stream to the virtual resource.
        /// </summary>
        /// <returns>A read-only stream to the virtual file.</returns>
        public override Stream Open()
        {
            // Create a stream.
            Stream stream = new MemoryStream();
            StreamWriter writer = new StreamWriter(stream);

            // Write to the stream.
            writer.Write("<%@ ServiceHost Language=\"C#\" Debug=\"false\" Service=\"silkveil.net.Wcf.Service\" %>");
            writer.Flush();

            // Reset the stream's pointer to its beginning and return the stream to the caller.
            stream.Seek(0, SeekOrigin.Begin);
            return stream;
        }
    }
}

Kompiliert man diese beiden Klassen mit in die DLL, die den WCF-Dienst erhält, genügt es für ein Deployment zukünftig, nur noch diese DLL zu verteilen. Auf diese Art ist es ein Leichtes, beispielsweise eine bestehende ASP.NET-Anwendung um einen WCF-Dienst zu erweitern – ohne mit einer .svc-Datei hantieren zu müssen.

Allerdings muss der VirtualPathProvider noch registriert werden, damit ASP.NET von seiner Existenz weiß: Dazu genügt es, diesen beim Start der Anwendung in der Datei Global.asax zu registrieren:

// Initialize the service activation provider.
HostingEnvironment.RegisterVirtualPathProvider(new ServiceActivationProvider());

Der entscheidende Punkt an dieser Vorgehensweise ist, dass Dateien, die von einem VirtualPathProvider in ASP.NET geladen werden, nicht – wie beispielsweise bei einem HttpHandler – direkt in den Ausgabestrom geschrieben werden, sondern dass sie zunächst noch von der ASP.NET-Engine verarbeitet werden.

Damit wäre es also auch möglich, an Stelle einer .svc-Datei eine beliebige andere Datei per Code bereitzustellen – .Webseiten, Usercontrols, … einzig Dateien, welche die gesamte Anwendung betreffen, bleiben außen vor, wie beispielsweise die Global.asax und die web.config.

Für alles andere gilt: Der Fantasie sind dabei keine Grenzen gesetzt …

Microkernel im Eigenbau

Freitag, 13. März 2009, 21:17 Uhr
Permalink | Kommentare (9) | Kommentare als RSSRSS

Ende November des vergangenen Jahres habe ich in Architektur lernen empfohlen, sich mit dem Einsatz eines Microkernels zu beschäftigen:

Auch dieses Thema fördert das Denken in Schnittstellen, allerdings auf einer anderen Ebene als die Entwurfsmuster - beziehen diese sich nämlich auf das Klassendesign, bezieht sich der Einsatz eines Microkernels auf das Komponentendesign einer Anwendung.

Außerdem habe ich behauptet, dass es allen bestehenden Microkerneln zum Trotz nicht schwierig sei, einen einfachen Microkernel selbst zu implementieren:

Zwar gibt es einige Projekte wie beispielsweise Unity, die man sich näher anschauen kann, allerdings ist ein einfacher Microkernel in wenigen Zeilen selbst implementiert, wobei der Lerneffekt dann allerdings weitaus größer ist.

Da ich seither immer wieder nach einem Beispiel gefragt werde, beschreibe ich nun hier, wie man einen eigenen Microkernel erstellt. Ich werde mich dabei auf das wesentliche Feature – nämlich die Instanziierung eines konkreten Typen an Hand eines gegebenen Contracts – beschränken, und Aspekte wie Fehlerbehandlung, Performance, Sicherheit und Konfigurierbarkeit außen vor lassen.

Letztlich sind folgende Schritte nötig, um einen eigenen lauffähigen Microkernel zu entwickeln:

  • Erzeugen einer Konfiguration, die das Mapping von Contracts auf konkrete Typen übernimmt. Es bietet sich an, diese Konfiguration in einer XML-Datei abzulegen, da diese erstens dank LINQ to XML mit wenig Aufwand ausgelesen werden kann, und dies zum anderen ermöglicht, die Konfiguration des Microkernels zu ändern, ohne das Projekt neu kompilieren zu müssen.
  • Instanziieren des für einen gegebenen Contract definierten konkreten Typs. Dies geschieht im Wesentlichen mit Hilfe der Activator-Klasse von .NET, wobei so wohl in das Projekt integrierte Assemblies wie auch zusätzliche Assemblies unterstützt werden sollen.

Zunächst muss also eine Konfiguration erzeugt werden, die das Mapping von einem Contract auf einen konkreten Typen übernimmt. Dazu dient folgende Struktur, die das Grundgerüst einer entsprechenden XML-Datei darstellt:

<?xml version="1.0" encoding="utf-8" ?>
<serviceLocator>
  <mappings>
    <mapping contract="" type="" />
  </mappings>
</serviceLocator>

Als einfaches Beispiel soll eine Instanz des Typs MemoryStream im Namensraum System.IO verwendet und auf die Schnittstelle IDisposable gemappt werden.

Hierzu müssen lediglich die Namen der beiden Typen in der XML-Datei angegeben werden, wobei diese vollqualifiziert  – also einschließlich des jeweiligen Namensraums – angegeben werden müssen:

<?xml version="1.0" encoding="utf-8" ?>
<serviceLocator>
  <mappings>
    <mapping contract="System.IDisposable" type="System.IO.MemoryStream" />
  </mappings>
</serviceLocator>

Im nächsten Schritt wird eine statische Klasse ServiceLocator erzeugt, die über die folgenden Methoden verfügt:

  • Einen statischen Konstruktur: Dieser ist dafür zuständig, die Konfiguration aus der XML-Datei auszulesen, und die dort angegebenen Namen der Contracts und Typen in jeweils eine Instanz der Type-Klasse zu überführen, und diese Zuordnungen danach in einem Dictionary abzuspeichern.
  • Eine statische Methode namens GetService: Diese Methode hat als einzige Aufgabe, den konkreten Typ für den angeforderten Contract aus dem Dictionary zu ermitteln, diesen zu instanziieren und entsprechend gecastet an den Aufrufer zurückzugeben.

Insgesamt ergibt sich daraus die folgende Grundstruktur für die Klasse ServiceLocator:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace CherryFlavored.NET.Core.Kernel
{
    /// <summary>
    /// Represents a service locator.
    /// </summary>
    public static class ServiceLocator
    {
        /// <summary>
        /// Contains the mappings from contracts to types.
        /// </summary>
        private static Dictionary<Type, Type> _mappings;

        /// <summary>
        /// Initializes the <see cref="ServiceLocator" /> type.
        /// </summary>
        static ServiceLocator()
        {
        }

        /// <summary>
        /// Gets an instance for the specified contract.
        /// </summary>
        /// <typeparam name="TContract">The type of the contract.</typeparam>
        /// <returns>An instance that matches the specified contract.</returns>
        public static TContract GetService<TContract>() where TContract : class
        {
        }
    }
}

Um die Konfigurationsdatei auszulesen und das Dictionary entsprechend zu erzeugen, bietet sich – wie oben bereits erwähnt – die Verwendung von LINQ to XML an:

// Load the configuration.
_mappings =
    (from mapping in
         XElement.Load("ServiceLocator.xml").Element("mappings").Elements("mapping")
     select
         new KeyValuePair<Type, Type>(
             Type.GetType(mapping.Attribute("contract").Value),
             Type.GetType(mapping.Attribute("type").Value))).ToDictionary(
        kvp => kvp.Key, kvp => kvp.Value);

Die Instanziierung des konkreten Typs in der Methode GetService ist sogar noch wesentlich einfacher, und kann in einer einzigen Zeile erfolgen:

// Instantiate the type and return it to the caller.
return (TContract)Activator.CreateInstance(_mappings[typeof(TContract)]);

Um nun tatsächlich eine Instanz der Klasse MemoryStream an Hand ihres Contracts zu erzeugen, genügt eine Referenz auf den Microkernel und folgende Zeile:

// Try to get an instance for an IDisposable type.
IDisposable memoryStream = ServiceLocator.GetService<IDisposable>();

Dieser Microkernel kann nun bereits verwendet werden, um Komponenten innerhalb eines Projektes zu entkoppeln. Häufig ist der zu instanziierende Typ aber gar nicht Bestandteil des eigentlichen Projektes, sondern liegt in einer eigenständigen Assembly, die potenziell erst nach dem Kompilieren zu einer bereits erfolgten Installation beigefügt wird.

Alles, was zur Kompilierungszeit in einem solchen Fall zur Verfügung steht, ist der Contract – der eigentliche Typ wird unabhängig davon bereitgestellt, um beispielsweise Addins zu ermöglichen.

Um ein solches Szenario zu unterstützen, muss der Microkernel also eine Möglichkeit bieten, einen Typ auch aus einer Assembly zu laden, die neben dem eigentlichen Projekt losgelöst im Dateisystem liegt, ohne dass sie bereits zur Kompilierzeit verfügbar war.

Das schöne ist: Dieses Szenario wird bereits unterstützt. Alles, was hierfür notwendig ist, ist, in der Konfigurationsdatei neben der Klasse auch noch die enthaltende Assembly anzugeben, wie folgendes Beispiel an Hand der Post-Klasse von BlogEngine.NET zeigt:

<?xml version="1.0" encoding="utf-8" ?>
<serviceLocator>
  <mappings>
    <mapping contract="System.IDisposable" type="BlogEngine.Core.Post, BlogEngine.Core" />
  </mappings>
</serviceLocator>

Mehr ist für einen eigenen Microkernel prinzipiell nicht notwendig. Natürlich empfiehlt es sich, diesen für einen produktiven Einsatz noch an etlichen Stellen zu optimieren und um weitere Funktionen zu ergänzen. Die grundlegende Funktionsweise dürfte aber klar geworden sein.

[Flags] – wozu?

Sonntag, 8. März 2009, 22:22 Uhr
Permalink | Kommentare (0) | Kommentare als RSSRSS

Die gängige Literatur wie auch entsprechende Webseiten zu C# und .NET empfehlen, eine Enumeration mit dem Attribut [Flags] zu kennzeichnen, sofern es sich bei dieser Enumeration um ein sogenanntes Bitfeld handelt – also um eine Enumeration, deren Werte in verknüpfter Form einen gültigen Zustand darstellen.

Eine häufig geäußerte Frage ist jedoch – welchen Zweck erfüllt dieses Attribut? Denn weder erspart es die Zuweisung geeigneter Zahlenwerte an die einzelnen Werte der Enumeration, noch beeinflusst es die Kombinierbarkeit der einzelnen Werte mit Hilfe bitweiser Operatoren in spürbarer Form.

Einzig die Repräsentation als Zeichenfolge, die mit Hilfe der ToString-Methode gebildet wird, verhält sich unterschiedlich. Doch welche Bedeutung hat das [Flags]-Attribut wirklich?

Laut MSDN besagt das [Flags]-Attribut:

Indicates that an enumeration can be treated as a bit field; that is, a set of flags.

Außerdem heißt es dort im Abschnitt Remarks:

Bit fields are generally used for lists of elements that might occur in combination, whereas enumeration constants are generally used for lists of mutually exclusive elements. Therefore, bit fields are designed to be combined with a bitwise OR operation to generate unnamed values, whereas enumerated constants are not. Languages vary in their use of bit fields compared to enumeration constants.

Dies bedeutet, dass es letztlich der jeweiligen Sprache obliegt, ob sie bitweise Operationen auf Enumerationen ohne gesetztes [Flags]-Attribut zulässt oder abweist. C# und Visual Basic lassen dies zu. In diesen Sprachen bewirkt das [Flags]-Attribut also wirklich nur eine Änderung im Verhalten der ToString-Methode. In anderen Sprachen könnte dies aber potenziell anders sein.

Diese Interpretation deckt sich auch mit einer Aussage des MVPs Jon Skeet, die er in einer Diskussion zu diesem Thema gemacht hat:

Not in C#, but another language *may* decide to only allow the & and | operators to work with enums declared as Flags.

Schlussendlich bedeutet das also, dass es für eine saubere Implementierung im Sinne der sprachlichen Interoperabilität notwendig und sinnvoll ist, das [Flags]-Attribut wie vorgeschlagen zu verwenden – obwohl man beispielsweise in C# und Visual Basic darauf verzichten könnte.

FogBugz

Mittwoch, 4. März 2009, 21:12 Uhr
Permalink | Kommentare (10) | Kommentare als RSSRSS

Seit einigen Jahren verfolge ich begeistert die Artikel von Joel Spolsky, die er in seinem Blog Joel on Software und seinen Büchern veröffentlicht. Seine Artikel sind ausgesprochen lesenswert, und kommentieren auf amüsante und zugleich nachdenkliche Art Entwicklungen, Technologien und Themen der IT.

Einige der Artikel ziehe ich seither immer wieder zu Rate, weil sie in gewissem Sinne zeitlos und allgemeingültig sind, wie beispielsweise Fire and Motion und The Joel Test: 12 Steps to Better Code.

Neben dem Schreiben seines Blogs ist Joel Spolsky zudem Gründer der Firma FogCreek, die er wie folgt charakterisiert:

We didn't start with a particular product in mind: our goal was simply to build the kind of software company where we would want to work, one in which programmers and software developers are the stars and everything else serves only to make them productive and happy.

Eines der Produkte von FogCreek ist FogBugz, eine webbasierte Projektmanagementsoftware, die Joel Spolsky unter anderem in seinem Artikel Painless Bug Tracking erwähnt. Auf Grund all dessen, was ich bisher über FogBugz gelesen habe, hätte ich die Software schon lange gerne eingesetzt - dem stand leider immer im Wege, dass sich die Kosten für mich als Privatperson nicht gerechnet hätten.

Seit Dezember 2008 arbeite ich nun aber mit Peter Bucher an einem gemeinsamen Projekt, für das wir neben Subversion als Versionsverwaltung auch ein Bugtrackingsystem auf Basis von .NET einsetzen wollten. Es sollte kostenlos verfügbar sein, trotzdem aber über alle relevanten Funktionen eines solchen Systems verfügen.

Während unserer Recherchen sind wir auf BugNET gestoßen - eine zwar kostenlose, aber wie sich inzwischen herausgestellt hat leider absolut instabile und fehlerhafte Software. Da wir jedoch auf keine wirkliche Alternative gestoßen sind, haben wir uns in den vergangenen Monaten damit herumgeschlagen.

In den vergangenen Tagen bin ich nun durch Zufall wieder einmal auf die Webseite von FogBugz gestoßen, das inzwischen nicht nur als Datenträger, sondern auch als von FogCreek gehostete Lösung angeboten wird - allerdings ist auch diese Lösung für uns als Privatpersonen zu teuer.

Jedoch habe ich noch eine weitere interessante Entdeckung gemacht: Die gehostete Version gibt es nämlich auch in einer sogenannten Student and Startup Edition, die für Einzelpersonen und Teams mit maximal zwei Teammitgliedern kostenlos zur Verfügung gestellt wird. Alles, was zu ihrer Nutzung notwendig ist, ist das Erstellen eines entsprechenden Accounts unter Angabe einer E-Mail-Adresse.

Der erste Eindruck ist ausgesprochen gut: FogBugz läuft dank intelligenter Nutzung von AJAX extrem schnell, arbeitet bislang fehlerfrei und ist vor allem ausgesprochen intuitiv bedienbar. Man merkt, dass die Software mit viel Liebe zum Detail erstellt wurde, und das Wort hochwertig trifft absolut zu.

Wer also noch auf der Suche nach einer geeigneten Projektmanagementsoftware für maximal zwei Teammitglieder ist, dem kann FogBugz nur uneingeschränkt empfohlen werden. Abschließend von meiner Seite aus noch ein großes Lob an FogCreek, und Danke für die kostenlose Bereitstellung!

Sinn und Zweck von AOP

Sonntag, 1. März 2009, 09:37 Uhr
Permalink | Kommentare (6) | Kommentare als RSSRSS

Am 13. Oktober 2008 haben Peter Bucher und ich unter dem Titel Noch Fragen, Bucher? Ja, Roden! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen. Bisher sind in dieser Reihe folgende Kommentare erschienen:

Heute, am 1. März 2009, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:

Sinn und Zweck von AOP

So wohl Peter wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Peters Kommentar findet sich zeitgleich in seinem Blog, folgend nun mein Kommentar zu diesem Thema:

Die aspektorientierte Programmierung (AOP) ist ein verhältnismäßig junges Programmierparadigma, das in gewisser Weise als Weiterentwicklung und Ergänzung der objektorientierten Programmierung gelten kann. Die der aspektorientierten Programmierung zu Grunde liegende Erkenntnis ist, dass eine Anwendung nicht nur solchen Code enthält, der sich direkt auf funktionale Anforderungen bezieht.

Statt dessen enthält jede Anwendung auch Infrastrukturcode, der den funktionalen Code um weitere Aspekte ergänzt. Diese Aspekte ergeben sich häufig aus den nicht-funktionalen Anforderungen und betreffen in der Regel Themen wie beispielsweise Sicherheit, Validierung, Transaktionen, Logging, Caching, ...

All diesen Themen ist gemein, dass sie für eine qualitativ hochwertige Anwendung notwendig sind, dass sie ohne den funktionalen Code jedoch nicht genügen, um eine eigenständige Anwendung zu formen. Als weitere Gemeinsamkeit kann genannt werden, dass es häufig nicht möglich ist, diese Aspekte genau einer Schicht zuzuordnen.

Da diese Aspekte also die vorhandenen Schichten vertikal schneiden, werden sie auch als Crosscutting Concerns bezeichnet, während der funktionale Code die Core Concerns abbildet. Die Frage, die mit Hilfe der aspektorientierten Programmierung beantwortet wird, lautet, wie diese Crosscutting Concerns isoliert entwickelt und an entsprechender Stelle effizient in die Anwendung injiziert werden können.

Die Sicht auf die aspektorientierte Programmierung hat sich dabei in den vergangenen Jahren drastisch verändert. Galt das Thema noch vor wenigen Jahren als ausgesprochen elitär, ist es inzwischen dank entsprechender Werkzeuge auf dem besten Weg, zu einer Commodity zu werden: In meinem Eintrag Architektur lernen habe ich bereits auf PostSharp und das zugehörige LAOS-Framework verwiesen, die .NET um einen Postcompiler und ein entsprechendes Framework zur aspektorientierten Programmierung erweitern.

Warum lohnt also die Beschäftigung mit aspektorientierter Programmierung? Die Antwort liegt auf der Hand, denn die aspektorientierte Programmierung vereinfacht und verkürzt die Entwicklung von Software in verschiedenen Phasen:

  • Planung: Um aspektorientierte Programmierung effizient einsetzen zu können, müssen potenzielle Aspekte zunächst identifiziert werden. Die Beschäftigung mit dieser Frage führt damit automatisch zu Überlegungen über die Architektur der zu entwickelnden Anwendung, potenziell allerdings unter einem neuen Blickwinkel, wodurch weitere Erkenntnisse über die Tragfähigkeit einer Architektur gewonnen werden können.
  • Entwicklung: Da die einzelnen Aspekte isoliert und unabhängig von der eigentlichen Anwendung entwickelt werden, sinkt der Entwicklungs- und Integrationsaufwand. Zudem können die Aspekte in eine eigenständige Komponente verpackt und für verschiedene Anwendungen wiederverwendet werden. Last but not least muss ein Aspekt unabhängig davon, wie oft er in einer Anwendung genutzt wird, nur einmal entwickelt werden, und kommt somit der Einhaltung des DRY-Prinzips entgegen.
  • Test: Aus den gleichen Gründen sinkt auch der Testaufwand für Aspekte, denn diese können unabhängig von einer Anwendung isoliert getestet werden. Da Aspekte zudem nur einmal entwickelt, aber mehrfach in die Anwendung injiziert werden, reichen einige wenige Tests aus, die in Konsequenz aber weite Bereiche der Anwendung abdecken.
  • Wartung: Schlussendlich muss bei einer Änderung eines Aspekts nur eine Codestelle geändert werden, was wiederum den vorigen Punkten zu Gute kommt - lediglich in einer neuen Iteration.

Doch wo Licht ist, gibt es bekanntlich auch Schatten: Eines der größten Probleme der aspektorientierten Programmierung ist derzeit, dass es (noch) keinen standardisierten Weg gibt, wie sie umgesetzt werden soll. Es fehlen etablierte Prinzipien, wie sie beispielsweise für die objektorientierte Programmierung existieren, die bei der Umsetzung helfen. Hier wird die Zukunft zeigen müssen, wie Best Practices aussehen werden und welche Ideen tragfähig sind.

Diesen Nachteilen zum Trotz überwiegen dennoch die Vorteile dermaßen deutlich, dass die Beschäftigung mit der aspektorientierten Programmierung in jedem Fall bedenkenlos empfohlen werden kann.