Als Änfänger fragt man sich immer:
"Wie baut man am besten die Struktur eines Programmes auf?".
Die Frage ist nicht auf Anhieb zu beantworten, da es immer von der Anforderung des zu erzeugenden Programmes abhängt, wieviele und welche Schichten man benötigt. Für weitere Informationen könnt ihr euch hier informieren. Ich möchte nun näher auf das drei Schichten-Modell in Kombination mit LightCore eingehen. Im folgenden eine Grafik, die das Ganze mal etwas verdeutlicht:

Wie man in der Grafik gut erkennen kann, sind die Schichten nur in direkter Richtung zueinander verbunden. Das hat so auch seine Richtigkeit, da z.B. die GUI nicht auf Inhalte des DataLayers zugreifen soll. Der Weg ist also klar vorbestimmt: von der GUI zum BusinessLayer und von dort zum DataLayer. In der Core-Schicht werden die einzelnen Interfaces für die jeweiligen Layer abgelegt.
Da wir hier eine mehrschichtige Architektur haben, ist es wünschenswert, dass die Abhängigkeiten untereinander so gering wie möglich gehalten werden. Das hat später den Vorteil, dass man ohne viel Aufwand einfach eine Schicht gegen eine andere austauschen kann. Genau an diesem Punkt kommt LightCore zum Tragen. LightCore ist ein sogenannter Inversion of Control Container (IoC Container), der auch in der Core-Schicht abgelegt wird. Weitere Informationen zum Thema IoC findet ihr hier. Inizialisiert wird er bei dem oberen Beispiel in der GUI. Im IoC werden alle Interfaces regestriert, damit man später auf die Objekte zugreifen kann. Wie das im Einzelnen geht, zeige ich später noch.
Durch diese Initialisierung muss allerdings eine Referenz auf den DataLayer eingerichtet werden, da sonst der DataLayer nicht abgedeckt ist. Somit liegt eine flexible drei Schichten Architektur vor. Nachfolgend eine Änderung im Schichtenmodell - links das Modell was wir schon kennen, rechts sieht man eine strenge drei Schichten Architektur.

Hinzugekommen ist in der neuen Variante die Schicht namens Framework. In dieser Schicht kann nun der IoC Container initialisiert werden, da Framework Referenzen auf alle Schichten hat. Soweit die Theorie, kommen wir nun zur Umsätzung innerhalb von Visual Studio. Das Schichtenmodell habe ich in Visual Studio wie folgt umgesetzt:

Wie weiter oben schon erwähnt, wurde eine Referenz im Core Layer auf die LightCore Assambly hinzugefügt. Weiterhin wurde eine Klasse mit dem Namen IoCContainer erstellt. Im IoCContainer werden alle Interfaces durch eine Regestrierung in der Klasse Initializer, die zur layerübergreifenden Kommunikation notwendig sind, gespeichert. Hier der Code der IocContainer-Klasse:
public class IocContainer
{
private static readonly ContainerBuilder _builder;
private static IContainer _container;
static IocContainer()
{
_builder = new ContainerBuilder();
}
public static void Register<TContract, TImplementation>() where TImplementation : TContract
{
_builder.Register<TContract, TImplementation>();
}
public static T Get<T>()
{
return _container.Resolve<T>();
}
public static void Build()
{
_container = _builder.Build();
}
}
Im folgenden noch die Klasse Initializer:
public class Initializer
{
public static void Initialize()
{
IocContainer.Build();
}
}
Ein geeigneter Ort um den IocContainer zu initialisieren ist der Standardkonstrukter in der starter Klasse auf der Form. In diesem Fall ist das die GUI und somit die Klasse MainWindow.xaml.cs.
public partial class MainWindow : Window
{
public MainWindow()
{
// IoC Container erstellen
Initializer.Initialize();
InitializeComponent();
}
}
Da wir jetzt das Grundgerüst unseres Programmes haben, zeige ich euch wie man eine neue Instanz einer Klasse erzeugen kann ohne den "new" Operator zu benutzen. Als erstes erzeugen wir auf dem CoreLayer einen Unterordner mit den Namen Contracts, darin errzeugen wir dann ein neues Interface. In meinem Beispiel nenne ich das Interface IanyRepository.

Jetzt implementieren wir das Interface auf dem DataLayer. Dazu erzeuge ich eine neue Klasse mit dem Namen AnyRepository auf dem DataLayer mit folgendem Inhalt:
public class AnyRepository : IanyRepository<string>
{
public List<string> getAny()
{
throw new NotImplementedException();
}
}
Da ich die Klasse mit der Methode getAny nur zu Testzwecken erzeugt habe, wird hier auch kein weiterer Code ausgeführt oder ein Wert zurückgegeben. Für die Demonstration des Beispiels ist das auch nicht weiter von Nöten.
Damit wir später eine neue Instanz erzeugen können, muss das Interface nun mit der dementsprechenden Klasse im IoCContainer regestriert werden. Dazu ergänzen wir die Klasse Initializer mit folgendem Code:
public class Initializer
{
public static void Initialize()
{
IocContainer.Register<IanyRepository<string>, AnyRepository>();
IocContainer.Build();
}
}
Zur Demonstration habe ich eine weitere Klasse auf dem BusinessLayer mit dem Namen AnyClass und folgendem Code erzeugt:
public class AnyClass
{
IanyRepository<string> repository = IocContainer.Get<IanyRepository<string>>();
public void AnyMethode()
{
List<string> liste = repository.getAny();
}
}
Jetzt kann man sehen, dass ich in Zeile drei eine neue Instanz der Klasse AnyRepository erzeugt habe. In Zeile sieben greife ich dann auf die Instanz zu und rufe die Methode getAny, die als Rückgabewert eine Liste mit dem Typ string zurück gibt, auf. Wie jetzt gut zu sehen ist, wurde das Ganze ohne den Operator new erreicht.
Ein weiterer Vorteil dieser Methode ist, dass man in der Klasse Initializer eine andere Klasse, die z.B. Beispielwerte zurückgibt (Mock-Klasse), ganz einfach mit dem gleichen Interface regestrieren kann. Beim Aufruf wird dann die Mock-Klasse aufgerufen und nicht die Klasse die später einmal vorgesehen ist. Zur Veranschaulichung hier noch ein Beispielcode aus der Klasse Initializer:
public class Initializer
{
public static void Initialize()
{
// Ursprünglicher aufruf der Klasse AnyRepository
//IocContainer.Register<IanyRepository<string>, AnyRepository>();
// Jetziger aufruf
IocContainer.Register<IanyRepository<string>, MockAnyRepository>();
IocContainer.Build();
}
}
Damit ihr selber herum experementieren könnt füge ich das Projekt als Download hinzu:
TierThreeModelDemo.rar (231,31 kb)
47688d9e-b10e-4f0c-87bd-3efce0469fd1|3|1.7
C#
LightCore, Drei Schichten Modell