Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

MadKat

Frischling

  • »MadKat« ist der Autor dieses Themas

Beiträge: 8

Wohnort: Schweiz

Beruf: Lernt Elektroniker

  • Private Nachricht senden

1

10.02.2014, 09:16

kleine Softwaredesignfrage C#

Hallo zusammen

Ich soll in meiner Ausbildung sozusagen eine kleine Abschlussarbeit machen. Ich muss ein Programm schreiben, welches ein Excel-File öffnet und die Stringbezeichnungen herausliest. Danach soll es in einem C#-Projekt suchen, ob diese Strings referenziert sind und am Schluss im Excel-File wieder anzeigen, ob sie vorhanden sind oder nicht.

Die eigentliche Realisierung ist nicht so schwer und habe ich auch schon gemacht. Allerdings wollte ich nun im Vorfeld eine gute und geeignete Softwarestruktur erstellen. Ich habe ein Klassendiagramm erstellt und wollte nun fragen, was ihr "Profis" davon haltet.

Vielen Dank für eure Mühen

MadKat
»MadKat« hat folgendes Bild angehängt:
  • StringFinder.png

Legend

Alter Hase

Beiträge: 731

Beruf: Softwareentwickler

  • Private Nachricht senden

2

10.02.2014, 09:52

Wenn ich das richtig lese erstellst du Interfaces und benutzt sie nie.
"Wir müssen uns auf unsere Kernkompetenzen konzentrieren!" - "Juhu, wir machen eine Farm auf!"

Netzwerkbibliothek von mir, C#, LGPL: https://sourceforge.net/projects/statetransmitt/

MadKat

Frischling

  • »MadKat« ist der Autor dieses Themas

Beiträge: 8

Wohnort: Schweiz

Beruf: Lernt Elektroniker

  • Private Nachricht senden

3

10.02.2014, 09:58

Wenn ich das richtig lese erstellst du Interfaces und benutzt sie nie.

Wieso benutze ich sie nie? Ich dachte ich mache das so, dass ich später noch andere Interfaces einfügen kann. Z.B. soll später evtl. ein Logger eingebaut werden oder die .cs Dateien sollten über die Projektdatei und nicht über das Verzeichnis gefunden werden. Ist das der falsche Ansatz?

Aber ich glaube ich weiss was du meinst. Mir nützen die Interfaces nichts. Ich habe nie 2 Sachen von einem Interface abgeleitet. :S

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

4

10.02.2014, 10:00

Ich finde es bedenklich, dass Deine Klassen wie Aufgaben heißen. Sie sollten wie Objekte heißen und die Methoden sollten Aufgaben sein.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

5

10.02.2014, 10:38

Ich finde, dass man in einem solchen Diagramm nur die Teile erfassen sollte, die für das jeweilig beschriebene System relevant sind.
Wenn man in deinem Fall die ganze Anwendung als gesamtes System ansehen würde, wäre eine Darstellung des gesamten Systems auch richtig. In deinem Fall handelt es sich aber wahrscheinlich um eine einfache Konsolenanwendung, bei der aus der Main-Methode heraus die anderen Komponenten verwendet werden. Es wird also sehr wahrscheinlich nie eine Instanz von Program erzeugt, weshalb nie mit entsprechenden Objekten hantiert wird, wodurch diese Klasse (aus Sicht der Objektorientierung) nicht von Bedeutung ist.
Du hast 3 (bzw. wenn man den Teil oberhalb von SearchString weglässt 2) statische Klassen, die auf Schnittstellen zugreifen. Die Methoden der statischen Klassen erwarten allerdings keine Instanzen der jeweiligen Schnittstellen, weshalb die statische Klasse entscheiden muss, welche Implementierung verwendet werden soll, wodurch die Schnittstellen grundsätzlich wieder unwichtig ist. (Wenn man nun mehrere Implementierungen hat innerhalb der jeweiligen Methoden die Entscheidung zwischen diesen getroffen und das erzeugte Objekt dann polymorph verwendet wird, dann könnte die Schnittstelle noch von Nutzen sein, da allerdings nur 1 Methode in allen statischen Klassen Parameter erwartet, können die anderen statischen Klassen höchstens über zufallsgenerierte Werte zu einer solchen Entscheidung kommen, was sehr wahrscheinlich nicht gewünscht sein dürfte.)
Die Methode SearchStrings liefert nichts zurück, obwohl der Name implizieren würde, dass ein Suchergebnis ermittelt wird und das die einzige Aufgabe der Methode ist. Genauso nimmt die Methode Write keine Parameter entgegen, obwohl ihr irgendwie mitgeteilt werden müsste, was wohin geschrieben werden soll. Sollte für letzteres die Variable Fileplace verwendet werden, dann sollte stattdessen entweder unbedingt ein Parameter verwendet oder darüber nachgedacht werden, aus der statischen Klasse eine nicht-statische Klasse zu machen. Für eine nicht-statische Klasse würde man einen Konstruktor anlegen, der Fileplace als Parameter entgegen nimmt, wodurch dieser nicht mehr den beiden Methoden übergeben werden muss.

Je 1 Klasse, statische Klasse und Interface sind bei dir schlecht benannt (SearchStrings, SearchRelevantFiles und SearchDirectory). Eine solche Benennung lassen sich für Methoden veerwenden, da die Namen erahnen lassen, dass eine Aktion ausgeführt wird. Da die Klassen (bzw. eigentlich deren Instanzen) in dem System die Akteure/Dinge/Subjekte darstellen, die Aktionen ausführen, sollte am Namen ablesbar sein, was diese Klassen (bzw. deren Instanzen) machen können (-> StringsSearcher, RelevantFilesSearcher und DirectorySearcher).


Vielleicht bin ich zu sehr die UML-Klassendiagramme gewohnt, allerdings erkenne ich kaum, was du mit diesem Diagramm überhaupt darstellen willst und nur scheint es auch etwas unvollständig zu sein. (Woher weiß die Methode Write was sie writen soll?) Auch ist anhand der Bezeichnungen nicht zu erkennen, wo in deinem Code nun der Teil realisiert wird, den du eigentlich umsetzen solltest.
Vielleicht wäre es aber auch besser, wenn du einfach mal ein UML-Klassendiagramm erstellen würdest. Wesentliche Unterschiede sind: es werden die Beziehungen zwischen Klassen modelliert (nicht die Aufrufe von Methoden), wodurch statische Klassen keine Beziehungen zu anderen Klassen haben.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

6

10.02.2014, 11:10

Meiner Meinung nach ein Overkill, für jede Methode eine eigene Klasse zu haben. Wenn es nur um den Lerneffekt und eine theoretisch mögliche Erweiterbarkeit geht, mag das ok sein.

Auf der einen Seite abstrahierst du "Excel" durch ein Interface, auf der anderen Seite rufst du aber direkt ValidExcel() auf. Wenn ich also Excel durch ein anderes IO-System ersetze, wird dann dennoch ValidExcel aufgerufen, weil die beiden Komponenten offensichtlich nicht zusammenhängen. Ich würde das Valid daher mit in die Klasse Excel nehmen (und auch in das dazugehörige Interface).

MadKat

Frischling

  • »MadKat« ist der Autor dieses Themas

Beiträge: 8

Wohnort: Schweiz

Beruf: Lernt Elektroniker

  • Private Nachricht senden

7

10.02.2014, 11:41

Vielen Dank für eure Antworten. Diese haben mir wirklich sehr geholfen. Ich verstehe, was ihr mir sagen wollt und habe dies nun nochmals überarbeitet. Das mit den Interfaces und mit den statischen Klassen habe ich im Nachhinein auch so gesehen wie ihr.
Ich habe die Klassennamen nochmals überarbeitet. Mein Problem ist einfach, dass es noch ein wenig erweiterbar sein soll. Wenn ich noch Zeit haben werden, wovon ich stark ausgehe, schreibe ich einen kleinen Logger. Ausserdem kann dann der Nutzer nicht nur einen Pfad eingeben sondern auch eine Projekt-Datei.

Was haltet ihr von dieser Grafik?

Nochmals vielen Dank für euer Bemühen.
»MadKat« hat folgendes Bild angehängt:
  • StringFinder2.png

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

8

10.02.2014, 12:33

Sollten ValidPath und ValidExcel ermitteln, ob der übergebene Dateipfad gültig war oder ob es sich hinter den Pfad um eine gültige Excel-Tabelle handelt, dann solltest du statt dieser Methoden eher Exceptions an den entsprechenden Stellen verwenden (im Konstruktor des Dateisuchers und in Read).
Ich habe es in der letzten Nachricht zwar noch nicht angemerkt, aber fileplace ist ein ungünstig gewählter Name. path oder filename sind in der Hinsicht besser geeignet. Wenn mann meherere Dateipfade übergeben will, dann benennt man diese i. d. R. auch nach ihrer Bedeutung (bspw. excelFileName oder projectRoot).
Was macht die Write-Methode? Woher weiß sie (da keine Parameter), was sie machen soll?

Deine Anwendung vollzieht dem Anschein nach 3 Schritte:
  1. Auslesen aller relevanten Zeichenketten
  2. Herausfinden, welche Zeichenketten verwendet werden
  3. Hervorheben der entsprechenden Zeilen in der Exceltabelle
Den ersten und letzten Schritt würde ich dabei am ehesten dem "ExcelHandler" zuweisen, auch wenn diese einen anderen Namen verdient hätte.
Beim Aufruf des Konstruktors könnte die Exceldatei ausgelesen und die entsprechenden Zeilen gesucht und deren Positionen vermerkt werden. (Die oben erwähnte Exception würde dann ggf. vom Konstruktor geworfen werden und die Methode Read würde entfallen) Über eine Methode GetFoo (ich weiß nicht, was in den Zeichenketten enthalten sind, daher kann ich keinen entsprechenden Namen anbieten) werden alle Foos bspw. als Stirng-Array zurückgegeben.
Wenn die Verarbeitung fertig ist, könnte dann entweder über ein HighlightFoo(String) oder ein SetFooHighlight(String, bool) bestimmt werden, welche Foos hervorgehoben werden sollen.
Mit einem Aufruf von Write könnte die Datei dann wieder an der gleichen Stelle gespeichert werden, von der sie ausgelesen wurde.
(Die Methode ValidExcel würde ebenfalls entfallen.)

Das Filtern der Zeichenketten lässt sich wieder unterteilen:
  1. Suchen aller Dateien
  2. analysieren einer jeden Datei
Für den ersten Schritt gibt es schon eine entsprechende Funktionalität. Da das Auslesen einer Datei sich auch einfach machen lässt, ist der letzte Schritt nur noch das Prüfen eines jeden Foo, ob es in dieser Datei vorhanden ist, um es sich dies dann zu merken. Da es keine Beteiligten weiter gibt (wie bspw. CSharpCodeFile oder FileFilter), kann man die Funktionalität in der gleichen klasse (statisch) ablegen, in der auch die Main-Methode liegt. (Zur optimierung sollte man beim Durchsuchen der Dateien eine Liste mit allen noch nicht gefundenen Foos und eine mit allen gefundenen Foos haben, die Dateien nur solange durchsuchen, wie man noch nicht alle durchgegangen ist und noch nicht alle Foos gefunden hat, und innerhalb der Dateien nur nach den noch nicht gefundenen Foos suchen. Bei einem Fund wird ein Foo dann von der einen in die andere Liste geschoben.)

Am Ende würde nur noch die Klasse übrig bleiben, die dein bestimmtes Exceltabellen-Format ("diese Zeilen und diese Spalten sind auf diese Art belegt") darstellt. Dass die anderen Dateien entfallen ist nicht weiter dramatisch, da sie bisher ohnehin eher so aussehen, als wären sie da, damit die entsprechende funktionalität in Klassen liegt.


Erweiterbarkeit:
Es ist zwar ganz hübsch, dass du dir darüber Gedanken machst, allerdings ist meine (und scheinbar auch die von einer paar anderen) eher die, dass eine Software erst einmal so implementiert wird, dass sie die Anforderungen genau abdeckt und erst später erweitert wird, wenn es auch erforderlich ist. In gewissem Maße ist es vielleicht sinnvoll, einen flexiblen Ansatz zu wählen, die Komplexität steigt aber, je flexibler die Lösung sein soll.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

MadKat

Frischling

  • »MadKat« ist der Autor dieses Themas

Beiträge: 8

Wohnort: Schweiz

Beruf: Lernt Elektroniker

  • Private Nachricht senden

9

10.02.2014, 13:51

Ersteinmal vielen Dank für deine ausführlichen Antworten Sacaldur.

Sollten ValidPath und ValidExcel ermitteln, ob der übergebene Dateipfad gültig war oder ob es sich hinter den Pfad um eine gültige Excel-Tabelle handelt, dann solltest du statt dieser Methoden eher Exceptions an den entsprechenden Stellen verwenden (im Konstruktor des Dateisuchers und in Read).
Ich habe es in der letzten Nachricht zwar noch nicht angemerkt, aber fileplace ist ein ungünstig gewählter Name. path oder filename sind in der Hinsicht besser geeignet. Wenn mann meherere Dateipfade übergeben will, dann benennt man diese i. d. R. auch nach ihrer Bedeutung (bspw. excelFileName oder projectRoot).
Was macht die Write-Methode? Woher weiß sie (da keine Parameter), was sie machen soll?

Du hast es erfasst. Das mit den Exceptions habe ich so übernommen. Fileplace wurde geändert. Bei der Write-Methode habe ich nun string[] übergeben um die fertige Liste anzuzeigen.

Deine Anwendung vollzieht dem Anschein nach 3 Schritte:
Auslesen aller relevanten ZeichenkettenHerausfinden, welche Zeichenketten verwendet werdenHervorheben der entsprechenden Zeilen in der Exceltabelle
Den ersten und letzten Schritt würde ich dabei am ehesten dem "ExcelHandler" zuweisen, auch wenn diese einen anderen Namen verdient hätte.
Beim Aufruf des Konstruktors könnte die Exceldatei ausgelesen und die entsprechenden Zeilen gesucht und deren Positionen vermerkt werden. (Die oben erwähnte Exception würde dann ggf. vom Konstruktor geworfen werden und die Methode Read würde entfallen) Über eine Methode GetFoo (ich weiß nicht, was in den Zeichenketten enthalten sind, daher kann ich keinen entsprechenden Namen anbieten) werden alle Foos bspw. als Stirng-Array zurückgegeben.
Wenn die Verarbeitung fertig ist, könnte dann entweder über ein HighlightFoo(String) oder ein SetFooHighlight(String, bool) bestimmt werden, welche Foos hervorgehoben werden sollen.
Mit einem Aufruf von Write könnte die Datei dann wieder an der gleichen Stelle gespeichert werden, von der sie ausgelesen wurde.
(Die Methode ValidExcel würde ebenfalls entfallen.)

Wieder richtig erkannt. ExcelHandler wurde in ExcelStringSearcher umbenannt und Read() wurde entfernt. Die Write-Methode kann man doch weglassen, wenn man bei der Highlight-Methode ein 2d-String-Array übergibt, mit den gefundenen und nicht gefundenen Strings.

Das Filtern der Zeichenketten lässt sich wieder unterteilen:
Suchen aller Dateienanalysieren einer jeden Datei
Für den ersten Schritt gibt es schon eine entsprechende Funktionalität. Da das Auslesen einer Datei sich auch einfach machen lässt, ist der letzte Schritt nur noch das Prüfen eines jeden Foo, ob es in dieser Datei vorhanden ist, um es sich dies dann zu merken. Da es keine Beteiligten weiter gibt (wie bspw. CSharpCodeFile oder FileFilter), kann man die Funktionalität in der gleichen klasse (statisch) ablegen, in der auch die Main-Methode liegt. (Zur optimierung sollte man beim Durchsuchen der Dateien eine Liste mit allen noch nicht gefundenen Foos und eine mit allen gefundenen Foos haben, die Dateien nur solange durchsuchen, wie man noch nicht alle durchgegangen ist und noch nicht alle Foos gefunden hat, und innerhalb der Dateien nur nach den noch nicht gefundenen Foos suchen. Bei einem Fund wird ein Foo dann von der einen in die andere Liste geschoben.)

Genau so habe ich das auch gedacht.

Am Ende würde nur noch die Klasse übrig bleiben, die dein bestimmtes Exceltabellen-Format ("diese Zeilen und diese Spalten sind auf diese Art belegt") darstellt. Dass die anderen Dateien entfallen ist nicht weiter dramatisch, da sie bisher ohnehin eher so aussehen, als wären sie da, damit die entsprechende funktionalität in Klassen liegt.

Ich habe aber immer noch zwei Klassen. Oder verstehe ich etwas falsch?

Erweiterbarkeit:
Es ist zwar ganz hübsch, dass du dir darüber Gedanken machst, allerdings ist meine (und scheinbar auch die von einer paar anderen) eher die, dass eine Software erst einmal so implementiert wird, dass sie die Anforderungen genau abdeckt und erst später erweitert wird, wenn es auch erforderlich ist. In gewissem Maße ist es vielleicht sinnvoll, einen flexiblen Ansatz zu wählen, die Komplexität steigt aber, je flexibler die Lösung sein soll.

Ja das mag vielleicht stimmen, aber wenn ich nun mehr Zeit habe, was ich haben werde, muss ich noch die optionalen Punkte im Lastenheft angehen. Dann muss ich wieder die Klasse RelevantFilesSearcher umschreiben...


Im Anhang ist mein angepasstes Diagramm :D
»MadKat« hat folgendes Bild angehängt:
  • StringFinder3.png

Werbeanzeige