101 Regeln Software-Designs OOP, Objektkomposition als Basis für ein Log Beispiel mit C#
0Ich hab in meinem letzten Beitrag über Basis Regeln beim Software-Design berichtet. In diesem Beispiel versuche ich zu zeigen wie man mit einer Objektkomposition eine elegante Lösung baut. Das ganze kommt aus der Praxis und ist eine Debug/Log Lösung. Ich hab das ganze sehr vereinfacht um euch die Idee und den Grund Gedanken zu zeigen. Wie man so was lösen könnte.
Ausgangslage:
Die Softwarelösung war zu diesem Zeitpunkt sehr instabil. Ich wurde eingestellt die Softwarelösung zu Supporten und zu verbessern. Ein Hauptproblem war die Performance bei den wichtigen Prozessen vom Unternehmen (Bussiness Logik). Die Entwickler hatten begonnen überall im Code Prints Anweisungen einzubauen weil die Softwarelösung in der Prod Umgebung sich auch anders verhalten hatte. Es wurde sogar so extrem das die lieben Leute durch das ständige rein und raus kopieren von diesen Debug Prints auch ungewollt Fehler machten. Das ganze war eine Intranet Weblösung und die wurde aktiv 24h verwendet. Zudem Stand das ganze in einer Hight Secure Zone vom Unternehmen, man hatte keinen direkten Zugang zu dieser Zone.
Im Team Meeting hatte ich den Vorschlag gemacht eine Debug Lösung einzubauen. Wo man Ein- und Ausschalten kann über das Webinterface. Das ganze ist unabhängig und erweiterbar zudem wird das ganze nur in den kritischen Bereichen ergänzt.
Was brauchen wir?
- Ein Interface in unserem Fall heisst es IDebug wo uns die Grund-Struktur für unsere Methode liefert.
- Eine Klasse wo die Arbeit übernimmt und die Debug Informationen abarbeitet. Zudem ist der Aufbau dieser Klasse vom Interface IDebug abhängig.
- Eine Methode in einer Bussiness Logik Klasse, in unserem Beispiel heisst die Klasse CurrencyBroker,
wo uns zeigt wie wir unsere Debug Lösung einbauen können. Das Interface liefert uns wider die Grundstrucktur.
Ich fange mit dem Interface an IDebug. Wo ganz einfach aufgebaut ist mit der Methode Debug. Wo als Argument einen string erwartet.
/**
* Interface Debug System
* */
using System;
namespace ch.starwolf.SoftwareDesign {
public interface IDebug {
void Debug(string arg);
}
}
Jetzt werden wir unsere Arbeiter Klasse definieren mit dem Klassennamen DebugLogger und der Implementation vom Interface IDebug. Ich habe weiter oben erwähnt das dass ganze erweiterbar ist. Wir sind genau an dieser Position jetzt, in unserm Beispiel wird unsere Klasse nur einen Output in die Konsole schreiben. Aber hier könnten wir weitere Klassen definieren wo zum Beispiel die Debug Informationen in eine Datenbank oder in ein XML schreibt. Wir könnten auch Klassen zusätzlich definieren wo uns ein Email zustellt oder ein RSS Feed abfüllt. Die Möglichkeiten sind fast unbegrenzt.
/**
* Debug job, write a log file
* */
using System;
namespace ch.starwolf.SoftwareDesign {
public class DebugLogger : IDebug{
public DebugLogger() {}
public void Debug(string Msg){
Console.WriteLine("Write Msg into a log file, Msg is : "+ Msg +DateTime.Now.ToString());
}
}//end of class
}
Jetzt kommt die Implementation in unsere Business Logik. Wir müssen nur eine Debug Methode definieren und beim Initialisieren ein IDebug Instanz durch-reichen. Die im Konstruktor von der Klasse mitgegeben wird. Die Business Logik hat somit nur mit ihren eigenen Instanzen zu arbeiten und ist nicht verantwortlich für das erstellen eines Debugger Objekts. Das ganze wir auch als Lose-Kopplung bezeichnet. Die Trennung ist dadurch klarer was nur Vorteile bringt. Das ist der ganze Aufwand um einen Debugger einzubauen, zudem bleibt die Implementation immer gleich auch für andere Business Logik Klassen. Das ganze sieht dann so aus.
/**
* critical Business logic
* */
using System;
namespace ch.starwolf.SoftwareDesign {
public class CurrencyBroker {
protected IDebug debugger;
public CurrencyBroker(IDebug debugger) {
this.debugger = debugger;
}
public void CurrencySale (string CC, int CCCount, int ID){
//do something
this.Debug("ID="+ID+" - CurrencySale - Done And Ready - ");
}
/* Debug Implementation */
protected void Debug(string Msg){
// if null no need for debug
if (this.debugger != null) {
this.debugger.Debug(Msg);
}
}
}//end of class
}
Der Aufruf und die Übergabe sieht so aus in unserem kleinen Beispiel.
/**
* Demo API
* */
using System;
using ch.starwolf.SoftwareDesign;
namespace ch.starwolf.SoftwareDesign {
class MainClass {
public static void Main(string[] args) {
Console.WriteLine("Run CurrencyBroker Job");
DebugLogger Debug = new DebugLogger();
CurrencyBroker job01 = new CurrencyBroker(Debug);
job01.CurrencySale("CHF",100,1);
}
}//end of class
}
Ausgabe der Konsole:
Run CurrencyBroker Job Write Msg into a log file, Msg is : ID=1 - CurrencySale - Done And Ready - 23.03.2010 19:34:30
Dazu noch ein UML Diagramm. Die roten Felder könnten zusätzliche Klassen sein.
Ich hab das Beispiel mit MonoDevelop und das Diagramm mit Dia unter Linux erstellt. Das kleine Projekt könnt Ihr hier runter laden.
Das war der ganze Zauber zu diesem Thema. Viel Spass beim Ausprobieren @all.
Hier noch ein paar Links zum Thema : Design Pattern, UML
