DevTrain Startseite Advanced Developers Conference vom 14.-15. Februar 2011  
  
  
SUCHEN:  
ARTIKEL ONLINE: 525   

Kategorien
.NET
Datenbanken
Web
XML

Allgemein
Camp
Foren
Events
Persönliche Einstellungen
Registrieren
Prämien Shop
Kontakt
Impressum
Über DevTrain

Autoren


   Autor: Klaas Wedemeyer Artikel Drucken
        
Mit einer ClassFactory die Teile einer Anwendung lose binden

Wenn man auf eine Klasse zugreifen möchte, erzeugt man normalerweise mit new ein Objekt und ruft auf der Referenz die Member auf.

MyClass A = new MyClass();
A.DoSomething();

Dieses hat leider das Problem, dass man die Klasse nur mit großem Aufwand später tauschen kann. An allen Stellen, wo man die Klasse in der Anwendung verwendet, muss man sie im Quellcode tauschen. Um etwas mehr Flexibilität zu erhalten, sollte man die Klasse loser binden: Als erstes darf man nicht auf die Klasse selber referenzieren, sondern auf ein Interface, welches alle nötigen Funktionsaufrufe zur Verfügung stellt. Die Klasse implementiert nun das Interface. Um das Objekt zu erzeugen, verwendet man eine ClassFactory, eine Funktion, die sich um die Erstellung des Objektes kümmert.

IMyClass A = ClassFactory.CreateMyClass();
A.DoSomething();

Im einfachsten Fall steht in der Funktion ein new Befehl. Mit der Änderung dieser Zeile kann dann man allen Programmteilen eine andere Klasse zur Verfügung stellen. Andere ClassFactories verwenden Konfigurationsdatein um zu festzulegen, welche Klasse welches Interface implementiert. Ich möchte hier eine ClassFactory vorstellen, die auf Assemblies basiert. Jede Assembly stellt eine Reihe von Klassen zur Verfügung. Es können dabei Klassen aus anderen Assemblies ersetzt werden.

- Die Anwendung wird durch die Zerlegung übersichtlicher.
- Kundenspezifische Änderungen können in entsprechende Kunden dlls ausgelagert werden. Gerade bei vielen Kunden erhöht das die Übersicht.
- Tools können problemlos ausgewechselt werden.
- Bei einer gut zerlegten Anwendung ist es leichter, einzelne Teile wieder zu verwenden.
- Die Logik kann von verschiedene Anwendungen benutzt werden (Web, WinForms, ?).
- Eine gut zerlegte Anwendung kann schnell auf mehrere Rechner verteilt werden. Man verwendet ein Interface und sieht nicht, ob es sich um die Klasse oder ein Proxy handelt.

Neben der Einzelregistrierung gibt es auch die Gruppenregistrierung. Hier können beliebig viele Klassen zu einem Interface registriert sein. Beim Aufruf muss dann entschieden werden, welche Klasse verwendet werden:

- Chain of Responsibility: Wenn man z.B. eine Datei lesen will, fragt man alle Klassen nacheinander, ob sie mit dem Format etwas anfangen können.
- Auswahl: Man bietet dem Benutzer in einer Liste alle Klassen (z.B.: Exportfilter) an und merkt sich die ID der ausgewählten Klasse, um diese später benutzen zu können.
- Ableitungen in der Datenbank: Für jede (Daten-) Klasse wird ein Service mit Interface erzeugt. Der Service implementiert das eigene Interface und das Interface des Services, der die Basis(daten)klasse verwaltet. Der Service wird hinter seinen eigenem Interface direkt registriert, hinter dem Interface der Basisklasse als Gruppe. Jeder Aufruf wird von der Klasse erst selber ausgeführt, dann schaut man in der eigenen Gruppenregistrierung nach, ob Ableitungen auch etwas erledigen können. Z.B. werden bei einem Select die Ergebnislisten zu einer zusammengefügt, beim Speichern überprüft man, ob man selber für das Objekt zuständig ist, sonst fragt man seine Ableitungen (da die Services nichts voneinander wissen, können die Dienste auch auf unterschiedliche Datenbanken zugreifen oder sogar Objekte ohne DB verwalten).

Um Klassen für die ClassFactory sichtbar zu machen, markiert man sie mit den ClassFactoryAttribute oder dem ClassFactoryGroupAttribute. Hier wird das Interface angegeben, das sie auch implementieren müssen. Hier können auch mit dem DeprecatedAttribute andere Klassen ausgeschaltet werden.
Im Initialize werden erst alle Assemblies nach allen Klassen durchsucht. Hierbei wird ein Dienst verwendet, der alle Assemblies der Anwendung zurück gibt.

List<Type> Classes = new List<Type>();
foreach (Assembly ass in Service.Assemblies)
{
 Classes.AddRange(ass.GetTypes());
}

Nun werden die Klassen nach den Attributen durchsucht.

foreach (Type Class in Classes)
{
 DeprecatedAttrribute[] DeprecatedAttrributes = (DeprecatedAttrribute[])Class.GetCustomAttributes(typeof(DeprecatedAttrribute), false);
 foreach (DeprecatedAttrribute attribute in DeprecatedAttrributes)
 {
  string key = attribute.Interface.FullName + " " + attribute.Class;
  if (!DeprecatedClassList.ContainsKey(key.ToUpper()))
   DeprecatedClassList.Add(key.ToUpper(), attribute.Class);
if (_List.ContainsKey(typeof(T))
   _List.Remove(attribute.Interface);
 }

 ClassFactoryAttribute[] ClassAttributes = (ClassFactoryAttribute[])Class.GetCustomAttributes(typeof(ClassFactoryAttribute), false);
 foreach (ClassFactoryAttribute attribute in ClassAttributes)
 {
  string key = attribute.Interface.FullName + " " + Class.FullName;
  if (!DeprecatedClassList.ContainsKey(key.ToUpper()))
   _List.Add(attribute.Interface, Class);
 }
}

Für die Erzeugung wird mit demType das Objekt erstellt

public static T GetObject<T>()
{
 if (_List.ContainsKey(typeof(T))
  return default(T);
 else
  return (T) Activator.CreateInstance(typeof(T));
}

Unter www.KlaasWedemeyer.de findet Ihr den ganzen Code und ein kleines Beispiel.

 


DevTrain Camp - Schneller zum .NET 3.5 Developer
 
Verwandte Artikel      Verlinkte Dokumente
    Keine verknüpften Dokumente
    Keine Links vorhanden

  Erfasst am: 24.08.2006
  Gültig bis: 22.11.2006
3 Ratings
Bewertung: 60,0%
schlecht    sehr gut  

 
© Copyright 2007 ppedv AG