Laden...

Anwendungsaufbau OOP? - konkret: genormte CSV-Dateien einlesen

Erstellt von jogisarge vor 15 Jahren Letzter Beitrag vor 15 Jahren 2.077 Views
J
jogisarge Themenstarter:in
154 Beiträge seit 2006
vor 15 Jahren
Anwendungsaufbau OOP? - konkret: genormte CSV-Dateien einlesen

Hallo zusammen,

ich arbeite mich gerade in C# mit VS2008 ein.
Das Galileo-Book VS 2008 habe ich auch.

Nun möchte ich meine Übungen anhand einer Anwendung machen, die ich benötige.

Die Anwendung soll genormte CSV-Dateien meiner Lieferanten einlesen und und in einer MS SQL-Server 2005 Express DB speichern.

So, nun lese ich die Dateien per Streamreader ein, splitte sie (";") und erhalte so ein Array pro CSV-Zeile.
eine While-Schleife liest Zeile nach Zeile und schreibt diese per SQL-Befehl in die DB.


StreamReader sr = new StreamReader("datei.csv");
sr.Read();
while (!sr.EndOfStream)
{
  string[] zeile = sr.ReadLine().Split(';');
  string sqlstatement = "INSERT INTO artikel(feld1,feld2,feld3,feld4,feld5)" +
		  "VALUES" +
  "('" + zeile[0].Trim() + "','" + zeile[1].Trim() + "','" + zeile[2].Trim() + "', " +
  " '" + zeile[3].Trim() + "','" + zeile[4].Trim() + "')";
  SqlCommand sqlins = new SqlCommand(sqlstatement, con);
  try
  {
     sqlins.ExecuteNonQuery();
  }
  catch (IOException ex)
  {
    System.Windows.Forms.MessageBox.Show("Fehler : " + ex.Message);
  }
}

Das passt auch alles soweit.
Nun zu meinem Problem :
Mir ist einfach unklar, wann ich das nach dem OOP Muster lösen soll, und wann nicht.

Mein Codeausschnitt ist etwas vereinfacht, da es sich normal um ca 40 Felder dreht, und diese müssen auf Inhalt geprüft werden.

Nun habe ich überlegt, eine Klasse CArtikel zu machen, und jedesmal ein Objekt Artikel zu erzeugen, wenn eine neue Zeile eingelesen wird.
Da taucht meine nächste Frag auf:
Wenn ich in der While-Schleife jedesmal

CArtikel artikel = new CArtikel();

eine Instanz erzeuge, ist die "alte" doch noch im Speicher, oder ?

Gruß jogi

2.187 Beiträge seit 2005
vor 15 Jahren

Hallo jogisarge,

Man würde die einzelnen Funktionen des Programms in eigene Klassen kapseln und diese nur noch in der Main-Methode verbinden (sieh auch Bild).

  1. CsvReader - Liest eine CSV Datei ein und überträgt die Felder in neue Artikelobjekte
  2. Artikel - Validiert die Artikeldaten
  3. O/R Mapper - Schreibt übergebene Objekte in die Datenbank.
    (Eventuell könnte man den CsvReader auch noch verallgemeinern, dass er verschiedene Formate und Objekte/Klassen einlesen kann.)

Klassen sollen NICHT mit C beginnen!!

C
90 Beiträge seit 2008
vor 15 Jahren

@Juyjuka, hab da Fragen zu deiner Antwort 🙂

Diese Verschachtelung von new'sen bereitet mir als Laie irgendwie Bauchweh. Könnte man nicht eine Klasse "csvVerarbeitung" erstellen, welcher man als Konstruktorparameter die Datei übergibt und die dann alle Notwendigen Methoden bereithält?

also so ähnlich wie:

csv = new csvVerabeitung("datei.csv);
csv.validiere();
csv.saveToDB();

Wenn das keine gute Lösung ist, dann vielleicht als statische Klasse, dass man das ganze so allgemein verwenden kann wie Console.Writeline?

Also

csvVerarbeitung.validiere(...)
csvVerarbeitung.speichere(...)

Was ich bei dir nicht verstehe, ist warum für jede Aktion eine ganz neue Klasse angelegt werden soll. Das Klingt so verschwenderisch irgendwie.
Gibt ja viele Methodensammlungen in Klassen, welche ihre Gebiete haben (Zeichnen etc.).

Danke

K
80 Beiträge seit 2006
vor 15 Jahren

auf den ersten blick ist es vll. verschwenderisch / aufwändig / unnütz. aber auf den 2. blick siehst du, dass du folgedes machen kannst:

Weg a) eine klasse die genau das tut was du willst

Weg b) Eine Datenbank Klasse, die immer wieder für den zweck hergenommen werden kann, eine CSV Klasse deren parameter bestimmen wie das array rauskommt, also welche Felder etc, kann dann auch für den zweck immer wieder hergenommen werden etc.

Verstehst das Prinzip? Reuse code 😉

656 Beiträge seit 2008
vor 15 Jahren

Sinn der Trennung ist eben (wie JayJuka bereits erwähnt hat) die Datenkapselung.

Wenn bei euch im Hause eine Lieferung von Waren reinkommt, wird die ja auch jemand übernehmen, eventuell einem Lagerarbeiter übergeben, der das ganze in die Regale packt, und dann den Lieferschein zur Ablage an die Buchhaltung weitergibt - ist selten die gleiche Person.

Hier hast du dann einen Verantwortlichen, der die Datei lädt und dich mit Artikeln versorgt. Das wäre der Lieferant, der die Päckchen aus seinem Lieferwagen nimmt und dir in die Hand drückt.
Dann gibts einen Verantwortlichen dafür, die Artikel in die Datenbank zu stopfen. Das wäre dein Lagerarbeiter, der genau weiß, in welches Regal die gehören.

Vorteil der Sache - der CsvReader von JayJuka gibt dir alle Artikel, die im StreamReader stehen. Die müssen aber nicht notwendigerweise in der Datenbank landen. Du könntest diese über das Netzwerk verschicken, in einer GUI anzeigen, oder nach XML umwandeln wollen.
Umgekehrt auch der O/R Mapper, der kann einen Artikel in die Datenbank schreiben. Der muss aber nicht notwendigerweise aus der Csv-Datei kommen, sondern könnte übers Netzwerk reinschwirren, vom Benutzer eingegeben werden...

Durch die Kapselung erreichst du einen höheren Wiederverwendungsgrad und eine höhere Spezialisierung. Dem Reader ist es egal, ob auf der anderen Seite ein GUI oder eine Datenbank steht, der geht hin und tut seinen Job (Csv lesen und Artikel zurückgeben).

Die Logik, die das ganze verbindet (Reader erstellen, Artikel holen, O/R Mapper füttern) könnte dann theoretisch deine csvVerarbeitungs-Klasse machen.
Da könnte man natürlich dann soweit gehen, die beiden Klassen (Reader und O/R Mapper) mitzugeben, dass du theoretisch auch einen anderen Reader reingeben könntest - aber das is eine andere Geschichte 🙂

Gruß, BhaaL

2.187 Beiträge seit 2005
vor 15 Jahren

Hallo carom,

Man könnte jetzt meine beiden Vorredner noch mal wiederhohlen aber dass lass ich bleiben. Beide haben es richtig erkannt und beschrieben.
(An dieser stelle Danke an Krieger und BhaaL. )

Gruß
Juy Juka

K
80 Beiträge seit 2006
vor 15 Jahren

Um es nochmal deutlich zu machen warum OOP und warum der Overheat:

Objekt Orientierung erhebt im prinzip den Anspruch darauf, Real existierende Zusammenhänge abzubilden. Irgendwie klingt das gerade komisch, aber das mag an der müdigkeit liegen. Fakt ist, dass das Klassen Design so generisch gehalten werden sollte, dass ein Objekt das abgebildet wird in jedem erdenklichen Anwendungsfall eingesetzt werden kann, nicht nur im kontext der aktuell entwickelten Applikation.
Dies führt zu dem Vorteil, dass klassen die einmal geschrieben wurden, nie wieder geschrieben werden müssen. D.h. hat du einen guten generischen CSV Parser, solltest du die klasse im idealfall in jeder deiner Applikationen wiederverwenden können ohne großartig nachdenken zu müssen, wie das Problem zu lösen ist. Im realen Leben ist das nie so ganz möglich, also entwickelte Klassen müssen meist noch erweitert werden, da man eben nicht jeden fall bedacht hat. Allerdings müssen die klassen wie gesagt nur ERWEITERT werden und nicht NEU geschrieben werden.
Für den Einsteiger wäre wichtig die Unterschiede der verschiedenen herangehensweisen zu kennen. Objekt Orientierte Programmierung ist nur eine von vielen herangehensweisen. OOP gilt z.Z. als der üblichste Ansatz, da o.g. Vorteile überwiegen. Allerdings gibt es auch Fälle in denen andere Ansätze wie Aspekt Orientierte Programmierung eingesetzt werden, ein relativ neuer Ansatz. Info Quelle hier wie immer Wikipedia, falls man sich weiter informieren möchte.

C
90 Beiträge seit 2008
vor 15 Jahren

Okay danke allen, so langsam macht es klick. Ich konzentriere mich bisher wohl zu stark auf den technischen Ansatz und nicht auf die Philosphie dahinter.

Das mit der Warenlieferung aus Bhaals Beispiel hab ich mir ähnlich vorgestellt. Allerdings hatte es für mich Sinn gemacht, das Warenhaus als Klasse zu beschreiben, denn es ist ja eine Einheit... Datenkapselung findet statt, weil niemand anders außerhalb des Warenhauses reinpfuschen darf. Lagerarbeiter und alle anderen Mitarbeiter waren Methoden, jeder hatte seine Aufgabe. Diese müssen nicht notwenigerweise etwas tun, sondern nur, wenn sie aufgerufen werden. Ob ich jetzt in eine DB oder in XML speichere oder was ganz anderes machen will, ist ja auch kein Problem, dann überläd man die Methoden halt.

Das war mein bisheriges Denken, kurz gesagt kann ich mich schwer von dem Gedanken trennen, das Warenhaus nicht als Klasse festzulegen, denn es verwaltet sich ja selbst mit seinen eigenen Daten und Methoden (Mitarbeitern) - wie im echten Leben auch.

Die Logik, die das ganze verbindet (Reader erstellen, Artikel holen, O/R Mapper füttern) könnte dann theoretisch deine csvVerarbeitungs-Klasse machen.

Vielleicht wird ja so ein Schuh draus. 🙂
(Aber macht man das generell überhaupt so, oder hast du das nur geschrieben, um jetzt speziell meine Vorstellung einer alles umfassenden Klasse doch noch umzusetzen, auch wenn es unüblich ist?)

K
80 Beiträge seit 2006
vor 15 Jahren

Macht man nicht so. Habe erklärt wie man OOP anwendet. Eine klasse für den O/R Mapper, eine zum Artikel holen, eine zum Reader / Writer verwalten.

2.187 Beiträge seit 2005
vor 15 Jahren

Hallo,

Hier mal das Ganze als Bild. Ich finde das immer besser verständlich.
(Falls man es nicht weis: Ein Kasten mit Name und Funktion(en) stellt eine Klasse dar.)

Gruß
Juy Juka

C
90 Beiträge seit 2008
vor 15 Jahren

Danke!

Krieger: Ich habe schon genau gelesen wie du das meinst, also O/R Mapper, Artikel holen, Reader / Writer in je eine extra Klasse.

(Es gilt: ich bin kein Sturkopf und mach das gerne so wie du meinst, aber lese bitte zu Ende bevor du jetzt genervt den Thread verlässt)

Aber warum kann man das ganze nicht wie von BhaaL vorgeschlagen in einer Überklasse verbinden? Die einzelnen Teile der Klasse sind ja selbst Klassen (wie von dir vorgeschlagen), Reusing Code klappt wunderbar. Okay, wenn man das so nicht macht lasse ich es, aber stelle dir vor:

Du willst Code fürs Web, für andere User zur Verfügung stellen, welcher eine wetter.xml von einer Wetterseite auf den lokalen Server des Users laden kann, bei Bedarf Updaten kann (weil veraltet) und letzten Endes auch auf der Website als html anzeigen kann.

Also erstellst du, wie du sagst, für alles eine seperate Klasse, ergo eine HoleXmlWetter Klasse, eine UpdateXmlWetter Klasse und eine ZeigeWetterAlsHtml Klasse zur Anzeige.

Der User muss jetzt aber für alle Aktionen mit unterschiedlichen Klassen agieren. Wäre es hier nicht sinnvoll, wenn der User hier eine Klasse hätte, die die anderen 3 beinhaltet und nichts anderes als wetter.hole(), wetter update() und wetter.show() kennen muss? Wäre doch gut für die Usability. Wenn man das von außen betrachtet bietest du eine kompakte Wetterklasse zum Downlaod an, statt 3 verschiedene, bei welchen der User noch selbst die Interaktion der Klassen dazucoden muss, was die Wetterklasse selbst erledigen könnte.

Und OOP soll ja nichts erschweren, sondern eigentlich das ganze vereinfachen.

2.187 Beiträge seit 2005
vor 15 Jahren

Hallo carom,

Das von dir beschriebene Konzept nennt man "Face-Pattern" oder "Fassade-Muster".
Dieses Musster muss sich jedoch dem Architektur-Muster unterordnen, nach dem die ganze Anwendung aufgebaut ist. Da die meisten Anwendungen (ich behaupte alle gut Designten) nach MVC- (MVVM-, etc.) oder Schichten-Architektur aufgebaut sind, würde die von dir Vorgeschlagene Fasade diese Architektur stören (.Show() kennt die UI und der rest ist BusinessLayer) und ist daher abzulehnen.

Aber im allgemeinen hast du recht, dass man das komplexe Zusammenspiel von verschiedenen Klassen als neue Klasse zusammen fassen kann.

Gruß
Juy Juka