Bewertung und Feedback zu meinem Programm

344 views
Skip to first unread message

Leba

unread,
Mar 29, 2013, 10:18:47 AM3/29/13
to clean-code...@googlegroups.com
Hallo,

ich wollte mal frage, ob ich ein wenig Feedback zu einem Programm bekommen könnte, dass ich zur Übung und Vorbereitung meiner Abschlussprüfung geschrieben habe. Die Aufgabenstellung ist in dem PDF anbei.

Ich habe u.a. Fragen zur Struktur des Programmes und zu CCD Aspekten.
Ist dies das richtige Forum dafür? Falls nein, welches ist gut geeignet?


Ich könnte es auf Wunsch auch anders verfügbar machen.

Anmerkungen:
- Ich programmiere noch nicht soo lange (;-))
- Es sind nicht alle Fehlerfälle behandelt und die Unit Tests decken nicht alles ab.
- Es geht mir vorwiegend um die Architektur und Struktur (nicht um den Algorithmus zur Lösung des Problems!)
- Ich habe eine 3 Schichtenarchitktur gewählt, die sich in den Namespaces wiederspiegelt (GroPro. Business, GroPro.Presentation, GroPro.Data)
- Die Presentationschicht ist eigentlich so gut wie überflüssig, aber ich habe es der Vollständigkeit halber so gemacht (ggf. overengineered?)
- Der Einstiegspunkt und das Komposieren und Initialisieren ist ausserhalb der Schichten (im Root Namespace: GroPro), bezeichne ich mal als "Querschnitts Komponente"
- Ich habe versucht so viel von Clean Code anzuwenden, wie möglich (Es gibt aber sicher noch sehr viele Verbesserungsmöglichkeiten)
- Es gibt keinen DI Container.

Meine Fragen bzw. Unsicherheiten:
- Ist der Einstiegspunkt so ok? 
        - Delegieren an anderes Objekt gut? 
        - Oder besser Einstieg im Presentation Layer? Problem: Dann muss das Initialisieren an die Querschnitts Komponente deligiert werden (weil Presentation darf nciht Data kennen).
- Ist das Komposieren und Initialisieren so sinnvoll? Oder eigene Klasse? (wegen SRP ja, oder?)
- Klassen die keine Attribute haben sind nicht statisch, aus Gründen der OO. Ist das ok, oder sollte man sie statisch machen?
- Es gibt ein paar Klassen, für die es keine Interfaces gibt und die man in den Tests deshalb nicht mocken kann. (ok/ schlecht?)
- Feeback zu Clean Code Aspekten
- Allgemeines Feedback und Anmerkungen

Vielen Dank!
Grüße leba

Aufgabenstrellung.pdf

Stephan Roth

unread,
Mar 29, 2013, 7:03:33 PM3/29/13
to clean-code...@googlegroups.com
Hallo Leba,

ich habe mich jetzt nicht besonders intensiv in die Fachlichkeit der Aufgabenstellung hineingedacht, daher erst einmal ein paar Anmerkungen, die mir bei einem ersten Blick in den Code aufgefallen ist.
  • Prüfe bitte einmal, welche using-Anweisungen du in den einzelnen cs-Files benötigst. Häufig kann man dort eine ganze Menge einfach so löschen, z.B. in der Program.cs
  • IMinimalStratagy sollte IMinimalStrategy heissen.
  • Einige Methoden machen nach meinem Gefühl zu viel (SRP!) und sollten der "Um zu..."-Regel folgend untergliedert werden. Beispiele: ConvertStackToPath und FindPathRec. Hier sollten noch kleinere Untermethoden gefunden werden, die über aussagekräftige Namen ihren Zweck deutlich machen.
  • Das Observer-Pattern findet nach meiner Beobachtung an mehreren Stellen Verwendung. Das ist so allgemein, das man es in abstrakte Klassen herausziehen kann und dort die ganze Listenverwaltung der Beobachter und der beobachteten Subjekte an einem zentralen Ort implementieren kann. Dann können die anderen Klassen diese abstrakte Implementierung erben.
Das erst mal so, was mir bei einem ersten Scan aufgefallen ist. Den Programmeinstieg finde ich gut so. Bei einigen Methoden war mir beim Lesen des Namens nicht klar, was sie tun.

Mock Up's (Testatrappen) setzt man in der Regel nur dort ein, wo man ein komplexeres Subsystem oder Teile der Infrastruktur, die entweder nicht immer verfügbar ist, oder die für den Test keine definierten Ergebnisse zurückliefern können, ersetzen muss.

Zur Schichtenarchitektur: empfängt das Programm Nutzereingaben, so müssten diese auch im Presentation-Layer abgelegt sein. Ansonsten fühlt sich dieses Architekturmuster wirklich etwas oversized für dieses relativ kleine Programm an. Würde ich aber jetzt nicht mehr ändern.

Soweit mein kurzer Überblick.

Viele Grüße,
Stephan



--
Sie haben diese Nachricht erhalten, weil Sie der Google Groups-Gruppe Clean Code Developer beigetreten sind.
Um Ihr Abonnement für diese Gruppe zu beenden und keine E-Mails mehr von dieser Gruppe zu erhalten, senden Sie eine Email an clean-code-devel...@googlegroups.com.
Wenn Sie Nachrichten in dieser Gruppe posten möchten, senden Sie eine E-Mail an clean-code...@googlegroups.com.
Gruppe besuchen: http://groups.google.com/group/clean-code-developer?hl=de
Weitere Optionen: https://groups.google.com/groups/opt_out
 
 

Ralf Westphal

unread,
Mar 30, 2013, 4:19:20 AM3/30/13
to clean-code...@googlegroups.com
hallo, leba!

ich hab mal kurz in deine lösung reingeschaut. zwei eindrücke hatte ich:

-da ist ein sauberer kern drin. was getan wird, ist recht klar zu erkennen: im calculator werden lösungen mit zwei strategien nacheinander gefunden und dann formatiert ausgegeben. (das kernproblem ist ja überschaubar und mit ein bisschen rekursion leicht zu lösen. das hab ich mir bei dir aber nicht näher angesehen.)

-der saubere kern ist in viel brimborium eingewickelt. die schöne klarheit ist nur schwer in all dem infrastrukturrauschen zu entdecken.

was soll die schichtenarchitektur? das pattern führt zu rauschen.
was sollen diese ganzen observer? das pattern führt zu rauschen - allemal, da .net events bietet.

auch schade, dass du keinen entwurf in für jedermann einsehbarer form beigelegt hast, z.b. pdf, jpg.

bottom line für mich: gute ansätze - aber overengineert. patterns waren wichtiger als die erfüllung nicht-funktionaler anforderungen wie z.b. produktionseffizienz oder lesbarkeit.

ich würd sagen: nochmal machen. quasi als application kata. (das PDF ist grottig. die beispiele sind kaum zu lesen.)
das kann ja nicht länger als einen tag dauern.
und dabei am besten auch inkrementelles vorgehen üben :-)

Ralf Westphal

unread,
Mar 30, 2013, 4:24:49 AM3/30/13
to clean-code...@googlegroups.com
Doch noch eine Frage zur Aufgabenstellung:

Wie ist "realistische Obergrenze" definiert. Es gibt dafür zwar ein Beispiel, aber ich verstehe den Gedankengang dahinter nicht.

Für "minimale Kosten" ist die Regel klar.
Aber "realistische Obergrenze"... ist das der zweitgünstigste Weg oder der drittgünstigste oder welcher?


Am Freitag, 29. März 2013 15:18:47 UTC+1 schrieb Leba:

Leba

unread,
Mar 30, 2013, 6:12:53 AM3/30/13
to clean-code...@googlegroups.com
Hallo,
erstmal vielen Dank an Stephan und Ralf für das Feedback.

Sorry für die PDF's ich hab sie gescannt und versucht die beste Qualität herauszubekommen. (Besser wäre nur gegangen, wenn ich es abgetippt hätte...)

Die Presentation Schicht und die Sache mit dem Observer Pattern ist overengineerd, das stimmt.

Allerdings finde ich die Aufteilung in Business (nur der Name könnte natürlich noch besser sein) und Data schon sinnvoll, schon um eine logische Trennung der Aufgaben zu erreichen.

Eine Dokumentation werde ich auch noch nachliefern.

@Ralf, es ging mir nicht so um das fachliche Problem, sonder mehr um die Struktur von dem Programm. Realistische Obergrenze ist nicht genau definiert. Ich habe es so verstanden, dass man einfach den kürzesten direkten Weg nimmt und dafür die Kosten berechnet. Hintergrund ist, dass dadurch der Backtracking Algorithmus deutlich schneller zu der optimalen Lösung kommt, weil die Obergrenze einen frühzeitigen Abbruch ermöglicht.

Ein weiterer Grund für die Verwendung der Schichtenarchitektur ist, dass ich anhand dieses sehr einfachen Beispiels die Schichtenarchitektur üben und besser verstehen möchte.

Mir fallen da gerade z.B. noch zwei konkrete Fragen ein:
1. IDataReadAccess z.B. ist ein Interface, was im Data Layer liegt. Rein logisch gehört es aber zum Business Layer. Dies kann man mit dem DIP begründen: Details sind abhängig von der Abstraktion und nicht umgekehrt. (2. Teil der Definition).
Aber im Business Layer darf es nicht liegen, denn sonst müsse Data ja Business referenzieren. Ist meine Lösung hinsichtlich dessen so trotzdem richtig?
2. Die Klasse Area ist im Root Namespace, da sie sowohl Data als auch Business kennen müssen. Alternativ könnte man Area in Data legen. Aber das finde ich nicht schön, denn es handelt sich dabei um eine Klasse, die ich eindeutig der Business Logik zuordnen würde. Man könnte als weitere Alternative die von Data nur einen String zurückgeben lassen und das Parsen dann im Business machen. Aber diese Aufgabe gehört nicht zu Business mE, denn es kann Business doch egal sein, in welchem Format die Daten geliefert werden, Business möchte einfach nur die Objekte bekommen, mit denen es seine Logik direkt durchführen kann.

Diese beiden Probleme klingen vielleicht nicht so besonders relevant. Sind sie aber meines Erachtens. In meinem Job haben wir eine sehr komplexe Applikation, die auch in Schichten aufgeteilt ist. Wenn man da keine klaren Vorstellungen hat, wie solche kleinen Dinge verwaltet werden, scheitert man an dem Großen Ganzen erst recht. Solche kleinen Fragen kommen ständig auf und es ist total wichtig, dass dies klar geregelt ist. Und ich möchte gerne diese Dinge lernen und mein Urteilsvermögen verbessern.

Leba

unread,
Mar 30, 2013, 8:44:47 AM3/30/13
to clean-code...@googlegroups.com
Hallo, 
anbei ein Architektur Diagramm. 
Das Layer Diagramm ist nicht nur eine zusammengeklilckte Grafik sondern lässt sich in VS validieren. (Ich habe sie auch in Git hinzugefügt.)
GroProArchitekturdiagramm.pdf

Ralf Westphal

unread,
Mar 31, 2013, 9:41:28 AM3/31/13
to clean-code...@googlegroups.com


Am Freitag, 29. März 2013 15:18:47 UTC+1 schrieb Leba:

Ralf Westphal

unread,
Mar 31, 2013, 9:42:07 AM3/31/13
to clean-code...@googlegroups.com
Am Samstag, 30. März 2013 11:12:53 UTC+1 schrieb Leba:

Allerdings finde ich die Aufteilung in Business (nur der Name könnte natürlich noch besser sein) und Data schon sinnvoll, schon um eine logische Trennung der Aufgaben zu erreichen.

ja, das mag sinnvoll sein. unzweifelhaft hast du ja aspekte: domänenlogik, UI und formatierung, dateizugriff.
daraus aber schichten zu machen... das tut nicht not.
 



@Ralf, es ging mir nicht so um das fachliche Problem, sonder mehr um die Struktur von dem Programm. Realistische Obergrenze ist nicht genau definiert. Ich habe es so verstanden, dass man einfach den kürzesten direkten Weg nimmt und dafür die Kosten berechnet. Hintergrund ist, dass dadurch der Backtracking Algorithmus deutlich schneller zu der optimalen Lösung kommt, weil die Obergrenze einen frühzeitigen Abbruch ermöglicht.

ich hätte so eine aufgabe zurückgewiesen :-) interpretieren soll man nix. das führt zu aufwand, der potenziell vergeblich war.

aber naja, es ging halt um programmstrukturen... und da hast du erkannt, dass es zwei verschiedene strategien gibt, eine lösung zu finden. die hast du beide mit dem selben interface abstrahiert. das ist entscheidend.

 

Ein weiterer Grund für die Verwendung der Schichtenarchitektur ist, dass ich anhand dieses sehr einfachen Beispiels die Schichtenarchitektur üben und besser verstehen möchte.

bei so einer aussage kann ich nicht mehr anders. jetzt muss ich es sagen ;-)

so eine schichtenarchitektur mit PL, BL, DL muss man nicht üben.
die halte ich für anachronistisch bis kontraproduktiv.


 

Mir fallen da gerade z.B. noch zwei konkrete Fragen ein:
1. IDataReadAccess z.B. ist ein Interface, was im Data Layer liegt. Rein logisch gehört es aber zum Business Layer. Dies kann man mit dem DIP begründen: Details sind abhängig von der Abstraktion und nicht umgekehrt. (2. Teil der Definition).
Aber im Business Layer darf es nicht liegen, denn sonst müsse Data ja Business referenzieren. Ist meine Lösung hinsichtlich dessen so trotzdem richtig?

siehst du. solche unnützen fragen kommen durch die schichtenarchitektur zustande.
du versuchst da etwas zuzuordnen, ohne dass das irgendeinen nährwert hätte.
ob die foo-funktionalität zur schicht bar gehört, macht nur sinn, wenn es überhaupt schichten gibt.
wenn man auf schichten verzichtet, kann man sich diese fragen sparen.

warum brauchst du oder man schichten? was war doch gleich der grund?
erklär doch mal.


 
2. Die Klasse Area ist im Root Namespace, da sie sowohl Data als auch Business kennen müssen. Alternativ könnte man Area in Data legen. Aber das finde ich nicht schön, denn es handelt sich dabei um eine Klasse, die ich eindeutig der Business Logik zuordnen würde. Man könnte als weitere Alternative die von Data nur einen String zurückgeben lassen und das Parsen dann im Business machen. Aber diese Aufgabe gehört nicht zu Business mE, denn es kann Business doch egal sein, in welchem Format die Daten geliefert werden, Business möchte einfach nur die Objekte bekommen, mit denen es seine Logik direkt durchführen kann.

und wieder solches unselige rumrätseln.
warum tust du dir das an?

 

Diese beiden Probleme klingen vielleicht nicht so besonders relevant. Sind sie aber meines Erachtens. In meinem Job haben wir eine sehr komplexe Applikation, die auch in Schichten aufgeteilt ist. Wenn man da keine klaren Vorstellungen hat, wie solche kleinen Dinge verwaltet werden, scheitert man an dem Großen Ganzen erst recht. Solche kleinen Fragen kommen ständig auf und es ist total wichtig, dass dies klar geregelt ist. Und ich möchte gerne diese Dinge lernen und mein Urteilsvermögen verbessern.

tja... was soll ich sagen?
total wichtig ist zunächst mal die frage, was das alles soll mit diesen schichten.
erst wenn es für die wirklich, wirklich einen grund gibt, dann musst du folgefragen stellen.

du hast aber nicht die frage beantwortet, warum du hier wirklich schichten brauchst. (für die komplexe app auf der arbeit wäre die frage auch zu stellen.)
du hast vielmehr die schichtung als gesetzt angenommen. so muss es sein - und daraus resultieren folgeprobleme.

betreib doch aber mal eine wurzelproblemanalyse. was ist denn angemessen als struktur?

was leistet schichtenarchitektur?
1. sie trennt aspekte
2. sie ordnet abhängigkeiten zwischen aspekten

die erste leistung ist gut - aber greift zu kurz. es gibt nicht nur 3 aspekte, sondern beliebig viele. der gute ansatz der schichtenarchitektur ist also einengend. du versuchst ständig alles auf 3 aspekte zu verteilen. du lässt nicht beliebig viele aspekte zu. das führt zu zeitfressenden fragen und zu merkwürdigen entscheidungen.

die zweite leistung ist gut - solange man denn noch dienstleistungsabhängigkeiten braucht: PL ruft BL, BL ruft DL.
aber genau das ist ja das problem. weil es solche abhängigkeiten gibt, wird software so schwer zu verändern.
deshalb: weg mit den abhängigkeiten. jedenfalls in dieser traditionellen und auf schichten beruhenden weise.
dann gibt es auch kein problem mehr mit richtigen vs falschen abhängigkeiten.

du warst mit den observern eigentlich auf einem guten weg... aber du hast dann die abzweigung verpasst.

wenn du dem "integration operation segregation principle" (IOSP) folgen würdest, dann bräuchtest du keine inhaltlichen schichten, sondern hättest nur zwei formale: integration und operation.

hier mal lesestoff für dich:

wenn ich für dein szenario eine lösung entwickeln würde, dann würden sich all die fragen, die du mühsam wälzt, nicht mal stellen :-)

Ralf Westphal

unread,
Mar 31, 2013, 10:28:50 AM3/31/13
to clean-code...@googlegroups.com
habe jetzt mal selbst einen entwurf gemacht, s. anhänge.

gropro1: ein system-umwelt-diagramm. darin sehe ich, dass ich zwei adapter brauche (rechts) und logik (mitte). das UI (links) ist vernachlässigbar.

von links nach rechts gelesen könnte man das als schichtendiagramm verstehen - aber so soll es nicht gelesen werden. die wesentliche trennung ist vielmehr außen/umwelt vs innen/domänenlogik.

hier gibt es 3 umweltaspekte, in anderen anwendungen könnten es 15 oder 50 sein. die in schichten zu zwängen, ist irrsinn. und in der mitte bei der domänenlogik kann es auch nochmal beliebig viele sub-aspekte geben.


gropro2: modellierung der funktionalität. es gibt nur eine interaktion des benutzers mit der anwendung: den start. dafür habe ich eine funktionseinheit "berechnen" modelliert. in die fließt der dateiname des "gebietsraster". das wars.

innen drin läuft die verarbeitung dann so:
1. raster aus datei laden
2. obergrenze der koste bestimmen
3. minimalkosten bestimmen
4. protokoll ausgeben

weiter verfeinere ich das modell nicht. der rest ist implementation. da kann man mit TDD ran gehen.

in dem modell ist mit farben allerdings noch eine zuordnung zu sehen. die funktionseinheiten sind klassen zugeordnet. (oder man könnte das auf dieser flughöhe auch als komponenten ansehen. wäre aber bei dir overengineert.)


gropro3: klassendiagramm. hier ist ganz deutlich zu sehen, dass es zwischen den operationen bzw. den klassen der operationen aus gropro2 keine (!) abhängigkeiten gibt.

es gibt nur eine abhängigkeit der integration ("berechnen") von den operationen. aber das ist völlig ok. die ist nicht funktional.

für die kostenberechnung habe ich mich doch für zwei klassen entschieden, die von einer basisklasse ableiten (oder ein gemeinsames interface implementieren).

alle anderen klassen brauchen keine interfaces. und komponenten sind auch nicht zwingend.

auf datenklassen habe ich verzichtet in der darstellung, die sind trivial. es gäbe vielleicht eine klasse für das gebietsraster. aber schon die pfade sind nur liste von punkten. dafür gibt es klassen.

das ist alles, was an entwurf nötig ist. (tut mir leid, wenn meine handschrift nicht gut leserlich ist ;-) es sollte aber schnell gehen. ein tool wie visio hätte mich behindert.)

der entwurf hat mich 5 minuten gekostet.
die programmierung würde mich jetzt vielleicht noch eine std kosten. (kommt drauf an, wie tief ich mich bei der rekursiven suche verheddern würde ;-)
eine tracer bullet implementation würde ich dem aber voran stellen, um zu zeigen, dass der entwurf funktioniert.
und ich würde in inkrementen vorgehen:

inkrement 0: tracer bullet: alles nur soweit mit code versehen, dass irgendwie etwas durch fließt und am ende herauskommt.
inkrement 1: raster in-memory, keine obergrenzenbestimmung, minimalkosten bestimmen, protokoll nur als dump ohne formatierung
inkrement 2: raster laden
inkrement 3: protokoll aufhübschen
inkrement 4: obergrenzenbestimmung

ich muss mir hier keine solchen fragen stellen, wie du. und nicht nur, weil die aufgabe so trivial ist.

schichtung ist überflüssig. integration von operation zu trennen, reicht völlig. abhängigkeiten von operationen gibt es nicht.

das ergebnis ist clean code.
gropro1.jpg
gropro2.jpg
gropro3.jpg

Stephan Roth

unread,
Mar 31, 2013, 12:35:30 PM3/31/13
to clean-code...@googlegroups.com
Hallo Leba,

vielleicht noch einmal ein paar Ergänzungen zu dem, was Ralf über die Schichtenarchitekturen gesagt hat.

Eine Architekturentscheidung ist immer etwas, was man im späteren Projektverlauf nur noch schwer, d.h. mit erheblichem Aufwand zurücknehmen kann, wenn sich dann herausstellen sollte, das die Entscheidung falsch war. Daher müssen Architekturentscheidungen auch immer gut begründet sein, sie müssen die von den Stakeholdern geforderten Qualitätsziele unterstützen. Einfach nur Schichten machen, nur weil es gerade in Mode ist, oder weil einige dieses Architekturmuster für eine Art Naturgesetz halten und (häufig auch in Ermangelung an Kenntnis anderer Architekturmuster) nichts anderes zulassen, ist immer hochproblematisch.

Wie Ralf es schon erläutert hat: Schichtenarchitekturen (Layer, n-Tier, ...) fassen bestimmte Aspekte oder Belange eines Softwaresystems möglichst kohäsiv (d.h. zusammenhängend) in einer Schicht zusammen. So sind z.B. in einer solchen Architektur häufig alle GUI-Aspekte (Bedienelemente, Anzeigen, Ein-/Ausgabe) auf einer Schicht, meistens Präsentationsschicht genannt, zusammengefasst. Wie schon gesagt: das Ganze erfüllt aber keinen Selbstzweck. Mit diesem Ansatz sollen Qualitätsziele wie beispielsweise Portabilität oder Verteilung unterstützt werden. Bei einem verteilten System kann die Präsentationsschicht auf einem anderen Rechnerknoten laufen als die anderen Schichten (Fachlogik- und Datenhaltungsschicht, um mal ein Beispiel zu nennen). Auch wird manchmal das Ziel genannt, das man später einmal ganze Schichten austauschen möchte. Möglich, das so etwas ab und zu mal vorkommt, aber tatsächlich habe ich das in freier Wildbahn bislang noch nicht erlebt. Daher ist das häufig YAGNI.

Ich sehe in Deiner Aufgabenstellung keinerlei Anforderungen oder Rahmenbedingungen, die eine Schichtenarchitektur begründen können. Genau genommen finde ich gar keine Qualitätsziele darin. Daher würde ich da auch sehr KISS herangehen.

Viele Grüße,
Stephan


Ralf Westphal

unread,
Apr 1, 2013, 4:19:42 AM4/1/13
to clean-code...@googlegroups.com, stephan....@googlemail.com

Eine Architekturentscheidung ist immer etwas, was man im späteren Projektverlauf nur noch schwer, d.h. mit erheblichem Aufwand zurücknehmen kann, wenn sich dann herausstellen sollte, das die Entscheidung falsch war.

Hm... da scheint mir etwas auf den Kopf gestellt: Architektur ist, was sich nur schwer verändern lässt. Puh... Das bedeutet, je weniger Ahnung ist habe, desto mehr Architektur betreibe ich? Denn wenn ich wenig Ahnung habe, dann ist ja am Ende alles nur schwer zurückzunehmen.

Ne, das kann nicht richtig sein.

Ich bevorzuge es simpler: Architekturentscheidungen betreffen nicht-funktionale Anforderungen.

Und die stets wichtigsten Fragen dabei sind:
1. Wie kann ich möglichst viele Anforderungen in einer Weise erfüllen, dass die Entscheidungen reversibel sind?
2. Wie kann ich einen Ausgleich zwischen widerstrebenden Anforderungen herstellen?

Erst danach kommen die konkreten Fragen zu Anforderungen an ein Softwaresystem.

Du siehst, für mich ist der Architekt derjenige, der sich bemühen muss, dass im Grunde nichts nicht zurückgenommen werden kann. Wie er das erreichen? Naja, das hat natürlich vor allem mit Prinzipien zu tun.


 
Daher müssen Architekturentscheidungen auch immer gut begründet sein, sie müssen die von den Stakeholdern geforderten Qualitätsziele unterstützen. Einfach nur Schichten machen, nur weil es gerade in Mode ist, oder weil einige dieses Architekturmuster für eine Art Naturgesetz halten und (häufig auch in Ermangelung an Kenntnis anderer Architekturmuster) nichts anderes zulassen, ist immer hochproblematisch.

Da stimme ich völlig zu. Die Entscheidung für ein Schichtenmodell (oder MVC oder sonst was) muss die Antwort auf eine Frage zu nicht-funktionalen Anforderungen eines Stakeholders sein.

Diese Anforderungen gab es Ende der 1990er mal landauf landab - nur sind die inzwischen weithin vergessen worden. Das Muster hat sich verselbstständigt. Es wurde so oft zurecht eingesetzt, dass man glaubt, es sei immer richtig. Dadurch hat man die Fähigkeit zur freien und angemessenen Entscheidung verloren.
 

Wie Ralf es schon erläutert hat: Schichtenarchitekturen (Layer, n-Tier, ...) fassen bestimmte Aspekte oder Belange eines Softwaresystems möglichst kohäsiv (d.h. zusammenhängend) in einer Schicht zusammen. So sind z.B. in einer solchen Architektur häufig alle GUI-Aspekte (Bedienelemente, Anzeigen, Ein-/Ausgabe) auf einer Schicht, meistens Präsentationsschicht genannt, zusammengefasst. Wie schon gesagt: das Ganze erfüllt aber keinen Selbstzweck. Mit diesem Ansatz sollen Qualitätsziele wie beispielsweise Portabilität oder Verteilung unterstützt werden. Bei einem verteilten System kann die Präsentationsschicht auf einem anderen Rechnerknoten laufen als die anderen Schichten (Fachlogik- und Datenhaltungsschicht, um mal ein Beispiel zu nennen). Auch wird manchmal das Ziel genannt, das man später einmal ganze Schichten austauschen möchte. Möglich, das so etwas ab und zu mal vorkommt, aber tatsächlich habe ich das in freier Wildbahn bislang noch nicht erlebt. Daher ist das häufig YAGNI.


Das sind mögliche Beweggründe für eine Schichtenarchitektur. Aber selbst die finde ich noch fehlleitend. Denn sie suggerieren, dass man nur eine ganze Schicht auf einen anderen Rechner verteilen können soll. Doch das ist wieder beschränkend (bis falsch).

Beispiel: Man entscheidet sich für ein Schichtenmodell und packt die Präsentationsschicht in Form einer ASP.NET Assembly auf einen Webserver. Hört sich ordentlich an, oder?
Dann läuft die Geschäftslogik in einem App-Server auf einem anderen Rechner. Ok?
Und im App-Server gibt es noch Datenzugriffsklassen, die auf einem SQL-Server auf einem dritten Rechner zugreifen. Ok?

Jetzt aber: Validation soll im Broser mit Javascript stattfinden. Und Aggregation soll mit einer SP im SQL-Server stattfinden.

Beides ist Geschäftslogik. Und die wandert nun in die Präsentationsschicht bzw. in die Datenzugriffsschicht?

Für einen Schichtenmodelldenker muss das einen Konflikt bedeuten. Der windet sich und versteht die Welt nicht mehr.

Für mich jedoch ist das überhaupt kein Konflikt - weil ich nicht in Schichten denke, sondern in Aspekten. Wenn die Aspekte sauber getrennt sind, dürfen Aspekte überall, auf allen Rechnern auftauchen. Das ist einfach eine Frage der nicht-funktionalen Anforderungen. Wenn Performance oder Skalierbarkeit es gebieten, dann bewege ich Logik so, dass die Anforderungen erfüllt werden. Ich verteile sie sogar beliebig über viele Rechner, jenachdem, wie es am besten passt.

Mit einer Schichtenarchitektur anzufangen, ist im besten Fall vorzeitige Optimierung. Im schlechteren Fall ist es einengend.

Aber das in Frage stehende Programm sollte ja zum Üben dienen. Hm... Ich würde sagen, Schichtenarchitektur muss man dann aber an einem Beispiel üben, wo sie Sinn macht. Das ist hier nicht der Fall. Also zuerst eine passende Aufgabenstellung suchen. (Leider fällt mir dazu wenig ein ;-) Ich glaube ja nicht, dass sie wirklich je echt sinnvoll war. Sie hat nur insbesondere Infrastrukturherstellern den Verkauf erleichtert. Damit konnte man seine Produkte besser verorten.)

Was man aber an diesem Beispiel üben kann, sind Techniken und Prinzipien, die zufällig auch bei einer Schichtenarchitektur vorkommen. Wenn es denn dringend sein muss, kann man hier IoC/DI üben. Vor allem kann man aber SoC/SRP üben.

Und ich bin natürlich der Meinung, dass man hier wie auch sonst IOSP und PoMO üben kann :-)

 
Ich sehe in Deiner Aufgabenstellung keinerlei Anforderungen oder Rahmenbedingungen, die eine Schichtenarchitektur begründen können. Genau genommen finde ich gar keine Qualitätsziele darin. Daher würde ich da auch sehr KISS herangehen.

Sehe ich auch so.

Allerdings KISS im Rahmen von Clean Code Development. D.h. nicht das allllllersimpelste tun, sondern auf Korrektheit und Evolvierbarkeit achten. Ein Entwurf wie ich ihn skizziert habe, gehört für mich dazu. Sonst hat man nur KISS auf Detailebene, aber nicht beim Big Picture.

 

Viele Grüße,
Stephan


Um Ihr Abonnement für diese Gruppe zu beenden und keine E-Mails mehr von dieser Gruppe zu erhalten, senden Sie eine Email an clean-code-developer+unsub...@googlegroups.com.

Leba

unread,
Apr 1, 2013, 1:08:01 PM4/1/13
to clean-code...@googlegroups.com, stephan....@googlemail.com
Wow, das ist viel Zeug... Und danke, Ralf, für den alternativen Entwurf und die beiden Links. Das ist wirklich sehr interessant!

Ich werde im Mai meine Abschlussprüfung bei der IHK haben. In dem Rahmen bekomme ich an dem ersten Tag die Aufgabenstellung und muss diese in einer Klausur bearbeiten.
Ein Mitglied des IHP Prüfungsausschusses fordert folgendes.
Das Konzept muss folgende Punkte enthalten:
1. Aufgabenanalyse
2. Verbale Beschreibung des Verfahrens (Algorithmen und Datenstrukturen)
3. Programmkonzeption unter Berücksichtigung der funktionalen Trennung nach dem 3 -Schichten-Model
  • Klassen, Methoden und Datenstrukturen in Form von UML
  • Sequenzdiagramme für die wesentlichen Abläufe
  • Detail. Beschreibung der Methoden in Form von Nassi-Schneidermanndiagrammen
  • Definition der Schnittstellen
Insofern kann man sagen, dass das 3 Schichten Modell eine Anforderung ist, auch wenn es so nicht direkt in der Aufgabenstellung steht.

Nach Abgabe der Klausur muss das Softwaresystem entwickelt, getestet und dokumentiert werden. Dafür hat man dann nochmal 4 Tage Zeit.

Ich möchte mich bestmöglich auf die Prüfung vorbereiten. Dabei möchte ich, und das ist eher ein Anspruch an mich selbst, so gut es irgend geht den CCD Prinzipien folgen. Dies wird sicherlich kaum bewertet, jedoch denke ich, dass clean code doch zumindest positiv auffallen sollte, da er nicht zuletzt leichter zu lesen ist.

Das Beispiel aus Ralfs letzten Post kommt mir sehr bekannt vor und ich merke, dass es tatsächlich Probleme mit der n-Tier Architektur gibt.

Trotzdem werde ich gezwungen sein sie zu benutzen und möchte dies wenigstens so gut es geht umsetzen.
Für die Prüfungen einen anderen Weg zu gehen halte ich für zu riskant. Also, wenn ihr bzgl. meiner Programmstruktur und meiner Fragen dazu noch Antworten habt, würde ich mich sehr freuen.

Definitiv werde ich mich mit den vielen Informationen aus diesem Post auseinandersetzen. Aber erst einmal kommt die Prüfung ;-) ...

Ralf Westphal

unread,
Apr 2, 2013, 4:36:16 AM4/2/13
to clean-code...@googlegroups.com, stephan....@googlemail.com
3. Programmkonzeption unter Berücksichtigung der funktionalen Trennung nach dem 3 -Schichten-Model

durchaus verständlich, dass sie sowas vorgeben. das ist halt lehre und nicht uni. da werden patterns gepaukt.
 
  • Klassen, Methoden und Datenstrukturen in Form von UML
  • Sequenzdiagramme für die wesentlichen Abläufe
  • Detail. Beschreibung der Methoden in Form von Nassi-Schneidermanndiagrammen
das ist allerdings großer quatsch. viel zu umständlich. sinniger pseudocode wäre besser. da solltest du nachfragen, ob das wirklich, wirklich zwingend ist.
 
  • Definition der Schnittstellen
Insofern kann man sagen, dass das 3 Schichten Modell eine Anforderung ist, auch wenn es so nicht direkt in der Aufgabenstellung steht.

dann frag doch mal "das mitglied", warum es 3 schichten sein sollen? was daran so vorteilhaft ist? welche nicht-funktionalen anforderungen dadurch erfüllt werden sollen? 

und wenn du die antworten hast, dann fragst du, ob du die anforderungen evtl. auch anders erfüllen dürftest.



Ich möchte mich bestmöglich auf die Prüfung vorbereiten. Dabei möchte ich, und das ist eher ein Anspruch an mich selbst, so gut es irgend geht den CCD Prinzipien folgen. Dies wird sicherlich kaum bewertet, jedoch denke ich, dass clean code doch zumindest positiv auffallen sollte, da er nicht zuletzt leichter zu lesen ist.

natürlich kann man auch eine mehrschichtige anwendung sauber aufsetzen. 3 schichten sind nicht dreckig. wenn es ist, wie du beschreibst, dann sind die eben eine anforderung, die du erfüllen musst. clean code development findet ja immer nur im rahmen von anforderungen statt.

 

Das Beispiel aus Ralfs letzten Post kommt mir sehr bekannt vor und ich merke, dass es tatsächlich Probleme mit der n-Tier Architektur gibt.

da bin ich beruhigt :-)

 

Trotzdem werde ich gezwungen sein sie zu benutzen und möchte dies wenigstens so gut es geht umsetzen.
Für die Prüfungen einen anderen Weg zu gehen halte ich für zu riskant. Also, wenn ihr bzgl. meiner Programmstruktur und meiner Fragen dazu noch Antworten habt, würde ich mich sehr freuen.

eines fällt mir noch ein: wenn schon 3 schichten, dann solltest du auch sauber noch die integration davon trennen.
ganz grob:

es gibt je eine klasse für PL, BL, DL.
und dann gibt es noch eine klasse, die das ganze zusammensteckt.

alle klassen tun wirklich nur das.
oder alle projekte, in denen die klassen sind, tun wirklich nur das.
du hast - wenn ich mich recht erinnere - irgendwelche ausgaben aber verstreut gehabt.

Leba

unread,
Apr 2, 2013, 6:18:53 AM4/2/13
to clean-code...@googlegroups.com, stephan....@googlemail.com

es gibt je eine klasse für PL, BL, DL.
und dann gibt es noch eine klasse, die das ganze zusammensteckt.

Warum je eine Klasse und nicht z.B. je ein Namespace?  

alle klassen tun wirklich nur das.
oder alle projekte, in denen die klassen sind, tun wirklich nur das.
du hast - wenn ich mich recht erinnere - irgendwelche ausgaben aber verstreut gehabt.

Ursprünglich hatte ich es so:
Die Klassen aus dem BL benachrichtigen ihre Obeserver, wenn eine Nachricht ausgegeben werden soll. PL beoabchtet den BL und ist für die Ausgabe zuständig. Ist das nicht ok?

Leba


Message has been deleted

Ralf Westphal

unread,
Apr 3, 2013, 4:17:20 AM4/3/13
to clean-code...@googlegroups.com, stephan....@googlemail.com

Warum je eine Klasse und nicht z.B. je ein Namespace?  

und was packst du in den namespace? eine methode?
ne, du kommst ja ohne klasse nicht aus.
ob die dann noch in verschiedenen namespaces sind, ist mir egal bei so einer kleinen anwendung.
du baust allein an dem ding herum. da kannst du auch alles in ein projekt schmeißen und einfach ein paar klassen aufmachen.

namespaces sind ein mittel, um massen von datentypen zu organisieren. du hast aber keine massen. alles ganz übersichtlich.

namespaces bauen hierachien. hierarchien sollten aber immer von unten wachsen. erst eine hierarchieebene einziehen, wenn mehrere dinge zusammengefasst werden sollten.

A, B, C - da lohnt keine hierarchieebene "Buchstaben".

A, B, 1, C, 2 - da könnten zwei oberbegriffe lohnen. aber eine ebene darüber lohnt noch nicht.

buchstaben(A, B, C)
ziffern(1, 2)

usw.

 

alle klassen tun wirklich nur das.
oder alle projekte, in denen die klassen sind, tun wirklich nur das.
du hast - wenn ich mich recht erinnere - irgendwelche ausgaben aber verstreut gehabt.

Ursprünglich hatte ich es so:
Die Klassen aus dem BL benachrichtigen ihre Obeserver, wenn eine Nachricht ausgegeben werden soll. PL beoabchtet den BL und ist für die Ausgabe zuständig. Ist das nicht ok?


vergiss doch mal das mit den oberservern. das ist so patternzwanghaft. du brauchst auch keine events/observer, um sauber nach IOSP/PoMO zu entwickeln. funktionen reichen. lies die blogartikel, die ich genannt habe.

das grundproblem, das du vermeiden solltest: abhängigkeiten. aber nicht zwanghaft.
wenn eine integration abhängig ist von operationen, dann ist das nicht schlimm. weil die integration deren ergebnisse nicht selbst verarbeitet. sie sorgt nur dafür, dass operationen zu einem ganzen zusammengeschaltet werden. die abhängigkeit ist also minimal. eine integration ist total simpel. da passiert einfach nix. keine logik.

operationen hingegen tun viel. da ist logik drin. deshalb sind sie eben nicht (!) von anderen abhängig.

IoC ändert nichts daran, dass es funktionale abhängigkeit gibt. deshalb greif IoC zu kurz. und damit ist auch DI keine rettung.
 

Leba

unread,
Apr 8, 2013, 4:42:17 AM4/8/13
to clean-code...@googlegroups.com, stephan....@googlemail.com
Wow, ich beginne gerade ansatzweise zu verstehen, was EBC ist.
Ich kann mir das zwar noch nicht so richtig vorstellen bei sehr komplexen Anwendungen, aber das kommt wohl noch.
Ich muss ich erstmal an die Denkweise gewöhnen. 
Das ist ja echt spannend! :-)

Leba

unread,
Apr 8, 2013, 12:56:19 PM4/8/13
to clean-code...@googlegroups.com
Ich habe mal einen ersten Versuch der Umsetzung gemacht: https://github.com/battermann/GroPro2.git
Sehr interessant, da die Struktur nun völlig anders ist als beim ersten Entwurf.

Ich weiß nicht, ob alles sinnvoll umgesetzt wurde. Ich weiß nicht, ob die Fehlerausgabe so gut ist oder nicht.
Auf jeden Fall habe ich den Eindruck, dass die Integration ganz schnell ganz schön unübersichtlich werden kann. Da braucht man sicherlich ein gutes Framework, welches einen dabei unterstützt.
Ich werde mich wohl noch mehr in das Thema einlesen müssen :-)

Leba

unread,
Apr 20, 2013, 6:51:11 AM4/20/13
to clean-code...@googlegroups.com
Ich habe nun einen Subflow "weggeklappt" und Funktionseinheiten in Methoden übersetzt, beispielhaft mit dem Subflow "AreaAusDateiEinlesen".

AreaAusDateiEinlesen ist also eine EBC und enthält drei Methoden: 
DateinamenErmitteln
StringAusDateiLesen
InAreaUmwandeln

Das gefällt mir aber nicht, da es gegen das SRP verstößt.
Ist "Funktionseinheiten in Methoden übersetzen" so gemeint?

Stephan Roth

unread,
Apr 20, 2013, 8:09:14 AM4/20/13
to clean-code...@googlegroups.com
Hallo Leba,

die Klasse "AreaAusDateiEinlesen" hat zwei Verantwortlichkeiten: zum Einen liest sie eine Zeichenkette aus einer Datei ein, und zum anderen konvertiert sie das in dieser Zeichenkette beschriebene Gebiet in ein Objekt vom Typ Area. Zumindest das sollte man noch trennen und der Klasse "AreaAusDateiEinlesen" einen abstrakteren und weniger nach einer Funktion klingenden Namen geben im Sinne von: was wird dort aus fachlicher Sicht getan? Vielleicht so etwas wie "Gebietserzeuger".

Der "Gebietserzeuger" verwendet nun einen "GebietsdefinitionsEinleser" und einen "GebietsdefinitionNachAreaKonvertierer" (in Deutsch hört sich das alles sehr sperrig an, daher bevorzuge ich immer englische Bezeichner). Das "DateinamenErmitteln" sieht sehr simpel aus, aber auch das könnte man noch in eine eigene Klasse auslagern; das ist zu empfehlen, wenn dort auch noch eine umfangreichere Fehlerbehandlung geplant ist.

Liebe Grüße,
Stephan


--
Sie haben diese Nachricht erhalten, weil Sie der Google Groups-Gruppe Clean Code Developer beigetreten sind.
Um Ihr Abonnement für diese Gruppe zu beenden und keine E-Mails mehr von dieser Gruppe zu erhalten, senden Sie eine Email an clean-code-devel...@googlegroups.com.

Leif Battermann

unread,
Apr 20, 2013, 8:18:48 AM4/20/13
to clean-code...@googlegroups.com
ja, das macht sehr viel Sinn und so in etwa war ja auch mein allererster Entwurf. (siehe oben: GroPro)

Dann habe ich ja einen zweiten Entwurf versucht mit EBCs. (siehe oben: GroPro2)

Kritik von Ralf am zweiten Entwurf war unter anderem, dass der Flow A -> B nicht unbedingt in EBCs übersetzt werden müsse, sondern dass man ihn auch in Methoden übersetzen könnte, also die Funktionseinheiten A und B nicht in EBCs sondern in A() und B() übersetzen

Aber so richtig verstehe ich nicht, wie das gemeint ist. Eben darauf zielt meine Frage ab. In GroPro3 habe ich versucht, das umzusetzen, jedoch gefällt es mir überhaupt nicht. Habe ich "Funktionseinheiten in Methoden übersetzen" nun richtig verstanden?




--
Sie erhalten diese Nachricht, weil Sie in Google Groups ein Thema der Gruppe "Clean Code Developer" abonniert haben.
Um Ihr Abonnement für dieses Thema zu beenden, rufen Sie die URL https://groups.google.com/d/topic/clean-code-developer/i_Oo8fyuoUw/unsubscribe?hl=de auf.
Um Ihr Abonnement für diese Gruppe und alle ihre Themen zu beenden, senden Sie eine E-Mail an clean-code-devel...@googlegroups.com.

Wenn Sie Nachrichten in dieser Gruppe posten möchten, senden Sie eine E-Mail an clean-code...@googlegroups.com.
Gruppe besuchen: http://groups.google.com/group/clean-code-developer?hl=de
Weitere Optionen: https://groups.google.com/groups/opt_out
 
 



--
Leif Battermann | Kuenstr. 9 | 50733 Köln | Mobil: 01573 8151460

Christof Konstantinopoulos

unread,
Apr 20, 2013, 2:20:00 PM4/20/13
to clean-code...@googlegroups.com
Hallo Leba,

schau mal in meine Masterarbeit zu Thema FlowDesign. Darin ist auch Grundlagen zu den EBCs. Eventuell helfen Dir die Infos weiter.

Hier der Link :
http://gso.konstantinopoulos.de/FlowDesignInDerPraxis_20120904.pdf

Viele Grüße,
Christof

Christof Konstantinopoulos

unread,
Apr 20, 2013, 2:26:50 PM4/20/13
to clean-code...@googlegroups.com
Korrektur - Es muss natürlich heißen : "Darin SIND auch Grundlagen zu den EBCs."

Leba

unread,
Apr 21, 2013, 4:32:58 AM4/21/13
to clean-code...@googlegroups.com
Vielen Dank, Christof.
Ich glaub, jetzt hab ich es. Hier: https://github.com/battermann/GroPro3.git nochmal ein neuer Versuch.
Ich glaube, es viel mir deswegen so schwer, da das mit den Methoden oberflächlich so schwer vom OO Entwurf zu Unterscheiden ist.

Eine Kleinigkeit:
Ich habe die FUs, die eine Verfeinerung von AreaCreator darstellen, in einen Ordner zusammengefasst.
Wenn ich später eine dieser FUs wiederverwenden will, so ist das unschön, denn die Klasse kann sich nur in einem Ordner befinden. Allerdings wäre es ebenso unschön, auf eine logisch Strukturierung zu verzichten.
Um Ihr Abonnement für diese Gruppe zu beenden und keine E-Mails mehr von dieser Gruppe zu erhalten, senden Sie eine Email an clean-code-developer+unsub...@googlegroups.com.

Wenn Sie Nachrichten in dieser Gruppe posten möchten, senden Sie eine E-Mail an clean-code...@googlegroups.com.
Gruppe besuchen: http://groups.google.com/group/clean-code-developer?hl=de
Weitere Optionen: https://groups.google.com/groups/opt_out
 
 
--
Sie erhalten diese Nachricht, weil Sie in Google Groups ein Thema der Gruppe "Clean Code Developer" abonniert haben.
Um Ihr Abonnement für dieses Thema zu beenden, rufen Sie die URL https://groups.google.com/d/topic/clean-code-developer/i_Oo8fyuoUw/unsubscribe?hl=de auf.
Um Ihr Abonnement für diese Gruppe und alle ihre Themen zu beenden, senden Sie eine E-Mail an clean-code-developer+unsub...@googlegroups.com.

Wenn Sie Nachrichten in dieser Gruppe posten möchten, senden Sie eine E-Mail an clean-code...@googlegroups.com.
Gruppe besuchen: http://groups.google.com/group/clean-code-developer?hl=de
Weitere Optionen: https://groups.google.com/groups/opt_out
 
 



--
Leif Battermann | Kuenstr. 9 | 50733 Köln | Mobil: 01573 8151460
--
Sie haben diese Nachricht erhalten, weil Sie der Google Groups-Gruppe Clean Code Developer beigetreten sind.
Um Ihr Abonnement für diese Gruppe zu beenden und keine E-Mails mehr von dieser Gruppe zu erhalten, senden Sie eine Email an clean-code-developer+unsub...@googlegroups.com.

Wenn Sie Nachrichten in dieser Gruppe posten möchten, senden Sie eine E-Mail an clean-code...@googlegroups.com.
Gruppe besuchen: http://groups.google.com/group/clean-code-developer?hl=de
Weitere Optionen: https://groups.google.com/groups/opt_out
 
 

--
Sie haben diese Nachricht erhalten, weil Sie der Google Groups-Gruppe Clean Code Developer beigetreten sind.
Um Ihr Abonnement für diese Gruppe zu beenden und keine E-Mails mehr von dieser Gruppe zu erhalten, senden Sie eine Email an clean-code-developer+unsub...@googlegroups.com.

Ralf Westphal

unread,
Apr 21, 2013, 4:48:05 AM4/21/13
to clean-code...@googlegroups.com
OOP und FD sind orthogonal.

Mit FD findest du zuerst Funktionseinheiten, die etwas tun. Die bringst du in eine passende Reihenfolge, um ein Ergebnis zu erzielen.

Erst in einem zweiten Schritt überlegst du dir, wie du darüber eine Abstraktionsschicht einziehst. Wenn du mit einer OOP-Sprache arbeitest, musst du das tun (Klassen in C#). In anderen Sprachen ist das nicht mal nötig (z.B. F#).

Wie Funktionseinheiten zu Klassen aggregiert werden sollten, ergibt sich aus Mustern und gefühlter Kohäsion.
Die drei von dir erwähnten Funktionseinheiten gehören für mich jedenfalls nicht in eine Klasse. Das sind drei Aspekte:

1. Zugriff auf Kommandozeile
2. Zugriff auf Dateisystem
3. Parsen von Daten

1 und 2 benutzen ganz unterschiedliche APIs für den Ressourcenzugriff.
3 steht am Übergang zwischen Ressourcen und Domäne. Das ist eine typische Map-Funktionalität. Die könnte sogar in die Datenstruktur verpackt werden.

class Area {
  public static Area Parse(string textualRepresentation) {...}

  public override string ToString() {...}

  ...
}

Und vergiss für den Moment mal EBC. Echt. Lass es sein. EBC sind eine besondere Form von Übersetzung von Funktionseinheiten. Dafür gibt es Anwendungsfälle. Aber einfacher ist es ohne.

Leba

unread,
Apr 21, 2013, 8:46:53 AM4/21/13
to clean-code...@googlegroups.com

OOP und FD sind orthogonal.

Mit FD findest du zuerst Funktionseinheiten, die etwas tun. Die bringst du in eine passende Reihenfolge, um ein Ergebnis zu erzielen.

Erst in einem zweiten Schritt überlegst du dir, wie du darüber eine Abstraktionsschicht einziehst. Wenn du mit einer OOP-Sprache arbeitest, musst du das tun (Klassen in C#). In anderen Sprachen ist das nicht mal nötig (z.B. F#).

Wie Funktionseinheiten zu Klassen aggregiert werden sollten, ergibt sich aus Mustern und gefühlter Kohäsion.
Die drei von dir erwähnten Funktionseinheiten gehören für mich jedenfalls nicht in eine Klasse. Das sind drei Aspekte:

1. Zugriff auf Kommandozeile
2. Zugriff auf Dateisystem
3. Parsen von Daten

1 und 2 benutzen ganz unterschiedliche APIs für den Ressourcenzugriff.

Ok, das habe ich mittlerweile verstanden.
 
3 steht am Übergang zwischen Ressourcen und Domäne. Das ist eine typische Map-Funktionalität. Die könnte sogar in die Datenstruktur verpackt werden.

class Area {
  public static Area Parse(string textualRepresentation) {...}

  public override string ToString() {...}

  ...
}

Also ich habe jetzt extra alle Logik aus Area und AreaPath herausgenommen und sie als reine DTOs konzipiert.
So kann ich ein Area Objekt durch die FUs durchreichen. Wenn ich das richtig verstanden habe, dann sollte man keine Objekte durchreichen, die Logik enthalten, sondern dann stattdessen diese als Ressourcen verwenden, oder?

In dem repos GroPro3 hab ich nun alles mit Methoden und Subflows umgesetzt. Kommt das denn nun einem sauberen Design nahe?

Grüße L






Ralf Westphal

unread,
Apr 22, 2013, 5:58:48 AM4/22/13
to clean-code...@googlegroups.com

class Area {
  public static Area Parse(string textualRepresentation) {...}

  public override string ToString() {...}

  ...
}

Also ich habe jetzt extra alle Logik aus Area und AreaPath herausgenommen und sie als reine DTOs konzipiert.
So kann ich ein Area Objekt durch die FUs durchreichen. Wenn ich das richtig verstanden habe, dann sollte man keine Objekte durchreichen, die Logik enthalten, sondern dann stattdessen diese als Ressourcen verwenden, oder?

Ich verstehe, dass du es richtig machen willst. "Richtig" ist nur leider dann doch nicht so einfach ;-)
Tendenziell zieht Flow-Design Daten und Funktionalität wieder auseinander im Vergleich zur Objektorientierung.
Aber das ist ja kein Zwang. Es geht auch bei FD um eine angemessene Verteilung von Verantwortlichkeiten.

Datenklassen (dazu gehören DTOs) haben die Verantwortlichkeit, für ihre Daten zu sorgen.
Dabei geht es zunächst mal um Konsistenz.

Die Frage ist nun: Wer baut die Datenobjekte auf?

Im Sinne einer simplen Serialisierung finde ich, kann das in Datenklassen selbst geschehen. Die kennen ihre Struktur am besten.
Um nichts anderes geht es in deinem Projekt.

Bei einer Serialisierung gibt es keine Abhängigkeit von irgendeiner Technologie. String rein, string raus.
Wenn ein Area aus einer DB kommen würde, dann wäre das etwas anderes. Dann wäre ein Repository (Factory) für den Aufbau der Datenstruktur verantwortlich.

Und natürlich dürfen "Objekte" jeder Größenordnung als Daten fließen. Das enthebt dich aber nicht des Nachdenkens, welche Funktionalität du wo verankerst.

Also: Versuche FD nicht "einfacher" zu machen, als es ist ;-)

Leif Battermann

unread,
Apr 22, 2013, 6:12:57 AM4/22/13
to clean-code...@googlegroups.com
Ok, gut! :-)
Dann verlagere ich das Serialisieren in die Datenklassen. Ebenso kann ich ja einfach wieder die anderen Funktionen, die vorher in den Datenklassen gekapselt waren (wie z.B. ctors, Rückgabe der Kosten eines Feldes und hinzufügen eines Zuges zu AreaPath etc.) und die ich rausgezogen habe, wieder hineinbringen...



--
Sie erhalten diese Nachricht, weil Sie in Google Groups ein Thema der Gruppe "Clean Code Developer" abonniert haben.
Um Ihr Abonnement für dieses Thema zu beenden, rufen Sie die URL https://groups.google.com/d/topic/clean-code-developer/i_Oo8fyuoUw/unsubscribe?hl=de auf.
Um Ihr Abonnement für diese Gruppe und alle ihre Themen zu beenden, senden Sie eine E-Mail an clean-code-devel...@googlegroups.com.

Wenn Sie Nachrichten in dieser Gruppe posten möchten, senden Sie eine E-Mail an clean-code...@googlegroups.com.
Gruppe besuchen: http://groups.google.com/group/clean-code-developer?hl=de
Weitere Optionen: https://groups.google.com/groups/opt_out
 
 

Leif Battermann

unread,
Apr 22, 2013, 7:20:50 AM4/22/13
to clean-code...@googlegroups.com
Hallo Christof,

bitte entschuldige, dass ich dich so direkt und persönlich anschreibe. Ich hoffe, dass das ok ist.
Ich habe einen großen Teil der Arbeit gelesen. Nur die Projektanalyse fehlt noch...
Ich fand es sehr informativ, interessant, verständlich ... einfach gut zusammengefasst und die Konzepte klar abgegrenzt.

Zu meinem Anliegen: Mein Unternehmen möchte gerne, dass ich eine Bachelorarbeit schreibe, die hauptsächlich den Entwurf und die Umsetzung eines Features im Rahmen unserer Software beinhaltet. Dies bringt für sie natürlich den größten (kurzfristigen) Mehrwert. Ich selber möchte mich aber auch zusätzlich mit einem Thema auseinandersetzen, was mich persönlich sehr interessiert.

Meine Frage an dich, da du dich ja schon sehr intensiv damit auseinandergesetzt hast, wäre: Denkst du, man könnte beides sinnvoll verbinden? Im Prinzip hast du ja genau das gemacht. Aber bei dem Projekt ging es ja um eine Standalone Software. Bei mir ginge es um ein Feature unseres Produktes. Die Fragestellung könnte, ins Unreine formuliert, lauten: Entwurf des Features "XY" und Gegenüberstellung zwei verschiedener Entwurfs-Strategien, dem herkömmlichen Design und dem Flow Design... (Da gibt es allerdings keinen großen Unterschied zu deiner Masterarbeit) oder Entwurf des Features "XY" und Beantwortung der Frage,lässt sich FD und "herkömmliches" Design in EINEM Projekt vereinen.. Meinst du, da gibt es noch interessante Aspekte, die du nicht schon in deiner Arbeit behandelt hast? Wie z.B. die Flow Execution Runtime?

Ich könnte mir die Fragestellung der Vereinbarkeit bzw. Kombination mit anderen (Mainstream) Paradigmen in einem Projekt ganz gut als Thema vorstellen.

Würde mich sehr über eine kurze Meinung dazu freuen.

Viele Grüße,
Leif




Christof Konstantinopoulos

unread,
Apr 22, 2013, 5:55:52 PM4/22/13
to Clean Code Developer
Hallo Leif,

Du brauchst Dich nicht entschuldigen. Ich sehe keinen Grund, warum Du
mich nicht einfach so anschreiben könntest. Allerdings hast Du mich ja
nicht direkt angeschrieben, sondern deine Anfrage in der Gruppe
veröffentlicht. Daher Antworte ich Dir auch hier :-)

Wenn ich die bisherigen Beiträge in der Gruppe richtig verfolgt habe,
dann hast du im Mai eine IHK Prüfung vor Dir. Gleichzeitig möchte dein
Unternehmen, dass Du eine Bachelorarbeit schreibst? Vermutlich haldelt
es sich also um eine Kombination von Lehre und Studium.

Eine Bachelorarbeit ist deutlich Praxisnäher als eine Masterarbeit.
Daher wird darin weniger Grundlagentheorie erwartet. Aus diesem Grund
würde ich deine erste Idee "Gegenüberstellung zweier verschiedener
Entwurfs-Strategien" nicht empfehlen. Du sagst selber, dass es dem
Unternehmen eher um die Umsetzung eines Features geht. Daher ist
Entwurf und Umsetzung des Features die Hauptaufgabe!

Die Entscheidung, WIE Du es umsetzt, liegt bei Dir! Und da spricht aus
meiner Sicht überhaupt nichts gegen die Anwendung des FlowDesigns.
Ich vermute, dass das Feature in eine bestehende Software eingefügt
werden soll. Also gibt es definierte Schnittstellen zu der bestehenden
Software. Diese stellen die äußere Grenze dar. Alles was innerhalb des
Features passiert, kann frei entworfen werden. Es wird für den Rest
der Anwendung ja nicht sichtbar.

Ein ganz kleines Beispiel für ein Projekt, welches innen FlowDesign
(bzw. EBC) verwendet und nach außen hin eine einfache API
Schnittstelle bietet, wurde 2011 von Stefan Lieser in der DotNet Pro
veröffentlicht. Hier der Link zum Online Archiv (auch für
Nichtabonennten verfügbar):
http://www.dotnetpro.de/articles/onlinearticle3839.aspx

Schau die das Projekt einfach mal an. Der Quellcode ist auch unter dem
Link verfügbar.

Lass Dich durch die EBC Strukturen und den ebc Compiler nicht
verwirren. Damals war das Flow Design noch nicht so weit. Daher ist
alles mit Event Based Components umgesetzt. Das muss aber nicht so
sein. Allein anhand der Grafiken kannst Du erkennen, was in der
MessageBus Komponente passiert. Und das ist gerade die Stärke des Flow
Designs. Man kann die Akivitäten sehr gut visualisieren und verstehen.
Selbst wenn man den zugehöhrigen Quellcode gar nicht versteht.

Daher sind Zeichnungen pflicht. Visualisiere den Flow! Das ist ein
großer Pluspunkt der Flow Execution Runtime. Es gibt dafür einen
Visualisierer. Das heißt die Flows werden als simpler Text definiert,
und automatisch in eine Zeichnung visualisiert. Aber es kommt auf die
Größe des Features an. Ich würde die Flow Execution Runtime erst ab
einer gewissen Projektgröße verwenden, da sie einige Einarbeitung
erfordert. Man kann die Flow Execution Runtime aber problemlos auch
für Teile einer Anwendung verwenden. Ich selber habe ein PlugIn für
das 3D Modellierungs Program Rhinoceros 3D damit umgesetzt.

Allerdings verwende ich noch immer gerne den EBC Ansatz, da mir die
Vorstellung von elektronischen Bauteilen entgegen kommt. Die
Verdrahtung der einzelnen Events im Quelltext fällt mir inzwischen
sehr leicht. Es gibt aber auch andere Ansichten, die gerade diesen
Verdrahtungsschritt kritisch sehen. Und Ralf hat ja mit der Flow
Executing Runtime gezeigt, dass dieser Schritt vollständig überflüssig
werden kann.

Am Ende ist es deine Entscheidung, aber ich sehe kein Problem darin,
den Flow Design Ansatz innerhalb eines "normalen" Programms
anzuwenden. Es gibt ja keine Abhängigkeiten, die zu Problemen führen
könnten.

Viele Grüße,
Christof Konstantinopoulos

Ralf Westphal

unread,
Apr 23, 2013, 4:26:53 AM4/23/13
to clean-code...@googlegroups.com
Dann verlagere ich das Serialisieren in die Datenklassen. Ebenso kann ich ja einfach wieder die anderen Funktionen, die vorher in den Datenklassen gekapselt waren (wie z.B. ctors, Rückgabe der Kosten eines Feldes und hinzufügen eines Zuges zu AreaPath etc.) und die ich rausgezogen habe, wieder hineinbringen...

naja, jetzt mal vorsichtig. was ist "etc."?

in datenklassen gehört, was die daten betrifft. mehr nicht.
das Area ist erstmal ein 2D array. was kann es da für dolle funktionalität geben?
es ist ein kostenarray. natürlich stehen dann in den feldelementen kosten.
und es sind auch start- und zielpunkt teil des area.

was bedeutet "zug hinzufügen"? was wird denn gezogen? das ist doch kein spiel.

ein pfad sehe ich nicht als teil des Area. der entsteht ja erst durch anwendung von logikregel.
pfade zu bilden und zu vergleichen ist im kern der domäne der ganzen anwendung.
sowas darf nicht in einer datenstruktur stecken. die logik darf nicht die datenstruktur sein.

aber: logik darf eine datenstruktur wie Area haben :-)
das ist ein unterschied: haben oder sein.

die logik kennt ein Area. aber sie selbst und nicht das Area bestimmt, wie pfade aussehen können.

wie gesagt:

var area = Laden("dateiname");
var pfade = Trailblazer(area);
var optimalerPfad = Optimum_Pfadfinder(pfade);
var vergleichspfad = Vergleich_Pfadfinder(pfade);

Trailblazer() ist Logik und hat ein area.
Die Pfadfinder sind andere Logik und haben pfade, die sie prüfen.

Weder trailblazing noch pfadfinderei sind teil von Area.

Also: Vorsicht!
 

Leif Battermann

unread,
Apr 23, 2013, 5:31:39 AM4/23/13
to clean-code...@googlegroups.com
ok, ich gebe zu, dass ich mich nicht ganz präzise ausgedrückt habe.
Allerdings ist das auch schwer, wenn man keine Romane schreiben will ;-)

Wir müssen aufpassen, dass wir nicht aneinander vorbeireden.

Das präziseste, was ich bieten kann ist der Code. (Siehe oben repos: https://github.com/battermann/GroPro3.git)

Ich denke, dass die Implementierung von GroPro3 dem, was du beschreibst sehr nahe ist.




--
Sie erhalten diese Nachricht, weil Sie in Google Groups ein Thema der Gruppe "Clean Code Developer" abonniert haben.
Um Ihr Abonnement für dieses Thema zu beenden, rufen Sie die URL https://groups.google.com/d/topic/clean-code-developer/i_Oo8fyuoUw/unsubscribe?hl=de auf.
Um Ihr Abonnement für diese Gruppe und alle ihre Themen zu beenden, senden Sie eine E-Mail an clean-code-devel...@googlegroups.com.
Wenn Sie Nachrichten in dieser Gruppe posten möchten, senden Sie eine E-Mail an clean-code...@googlegroups.com.
Gruppe besuchen: http://groups.google.com/group/clean-code-developer?hl=de
Weitere Optionen: https://groups.google.com/groups/opt_out
 
 

Ralf Westphal

unread,
Apr 24, 2013, 4:59:22 AM4/24/13
to clean-code...@googlegroups.com

Das präziseste, was ich bieten kann ist der Code. (Siehe oben repos: https://github.com/battermann/GroPro3.git)

Ich denke, dass die Implementierung von GroPro3 dem, was du beschreibst sehr nahe ist.

das sieht schon sehr ordentlich aus.

ein paar "zusammenfassungen" sind mir nicht so klar (was hat das lesen von zeilen aus einer datei mit area parsing zu tun?), aber ansonsten geht das doch in die richtige richtung.

interfaces benutzt du nicht, aber machst DI. das finde ich überflüssig. dein code wäre übersichtlicher, würdest du darauf verzichten.

die integrationen hätten auch nicht in eigene klassen ausgelagert werden müssen. aber ist schon ok :-)

die wichtigste frage ist: merkst du einen (positiven) unterschied, nun da du es so gemacht hast, gegenüber früher?


Leba

unread,
Apr 24, 2013, 3:16:07 PM4/24/13
to clean-code...@googlegroups.com


die wichtigste frage ist: merkst du einen (positiven) unterschied, nun da du es so gemacht hast, gegenüber früher?


Der Code ist aufgeräumt und klar. Durch die Möglichkeit des Betrachtens verschiedener Abstraktionsebenen ("Hinein- und Herauszoomen") ist das Programm sehr verständlich.
Und vor Allem ist schön, dass dieses Ergebnis erzielt wird durch ein klar strukturiertes Vorgehen, dem Flow Design. Es fällt also nicht vom Himmel!

Ich sehe allerdings 2 der SOLID Principles komplett missachtet. Aber solange es keine Anforderungen gibt, die z.B. Erweiterbarkeit fordern bzw. Änderungen wahrscheinlich werden lassen, ist das wohl ok.
Außerdem ist Testbarkeit ja auf jeden Fall gegeben. Und das mit den SP ist wohl auch nicht so ganz tragisch, wenn ich das richtig sehe, da es ja nur die integrierenden Komponenten betrifft, in denen ja eh keine Logik stecken sollte.
Reply all
Reply to author
Forward
0 new messages