moin moin,
So diesesmal ein etwas kürzerer Beitrag
Gliederung:
0. Allgemeines
1. Zitate
2. Weitere Ausführungen oo vs pop
3. Definition von pop und oo
4. Was mich an oo stört (Zusammenfassung)
5. Fallbeispiel
6. GoF und Sonstiges
7. Schlusswort
0. Allgemeines:
Da mir jetzt schon öfters aufgefallen ist, dass ihr einen Bezug zu PHP herstellt.
Ich möchte eher allgemeine Paradigmen von oo und pop besprechen und nicht irgendwelche auf irgendeine Sprache bezogene Paradigmen
Vlt. erkennt ihr auch daran mein Denkmuster. Möglichst generisch und flexibel programmieren. Und genau da macht mir oo irgendwie einen Strich durch die Rechnung (vermutlich)
1 Zitate:
Ja genau Service! Die Logik bzw. das Verhalten wird schön in einer extra Klasse bzw. prozedur gekapselt und nicht dezentral aufgeteilt. Ist ganz meiner Meinung!
Das Prinzip von Erweiterungsklassen hab ich hier aber nicht wirklich verstanden, welchen Mehrwert hab ich denn hier?
Wenn ich jetzt ausgehend vom User wissen möchte ob er gebannt ist, dann muss ich die Erweiterungsklasse derefernzieren, ok! Spinnt man das ganze aber weiter, dann könnte es am Ende so aussehen, dass ich ne class user hab mit nix drinnen und nur Erweiterungsklassen draussen herum baue?!?
Und da schon die nächste Frage, woher bekommt der UserBanCtrl die Referenz auf diese Erweiterungsklasse. In deinem Beispiel von der Factory ganz frisch. Aber normalerweise wohl doch dann von einer globalen Stelle, einer Registry oder global von der Datenbank, oder?
Würde mich über weitere Ausführungen hier freuen....
Ich vermute du sprichst hier sowas wie DI an? Ein anderes Verhalten einspritzen.
Dazu muss ich nochmal etwas ausholen.
Assembler: indirekte Addressierung:
lese ich einen indirekten wert bspw. aus einem register (äh quatsch ist glaube ich sogar semantisch verboten aus nem register indirekt zu dereferenzieren)..... also aus ner Speicherstelle, dann ist der Wert den ich lese die Referenz auf eine weitere Speicherstelle und lese diesen dann!
entspricht diese Speicherstelle nun einer Funktion, so kann ich während des Laufen des programms an der ursprünglichen Stelle, also an der Stelle die dann die Dereferenzierung angibt einen anderen Wert eintragen, der dann dafür sorgt, dass eine andere Funktion angesprungen wird.
In PHP funktioniert das glaube ich mittels call_user_func oder so.....
in C wird ein Funktionspointer dereferenziert
Und in der OO-Programmierung ist es die polymorphie, die bspw. mittels interfaces umgesetzt wird
=> Machen aber alle vier semantisch das gleiche!
Um bspw. später if else zeugs zu vermeiden, setze ich einmalig (bzw. später auch änderbar) so einen Funktionspointer.
In obigen Beispiel gibt der dann die "Bann-Rgeln"-Strategie vor!
Die Frage ist nur, WO ich diesen Funktionspointer abspeichere?
An einer globalen Stelle (oo und pop möglich), oder lokal (nur oo möglich)
Und hier muss ich dir tatsächlich recht geben, dass es wohl strukturierter Ist das Verhalten direkt im objekt abzuspeichern.
Aber das ist hier auch das einzigste was OO "besser" kann als pop.
Naja hier kommt ein klein wenig Zustand dazu, was aus dem Service ohne Zustand einen Service mit etwas Zustand macht.
Aber die Kernproblematik besteht hier weiter, wir verwenden einen Service, der eher global agiert!
Und für mich stellt sich die weitere Frage, von woher bekommt der Service seine Strategien? Meine Antwort darauf wäre von einer globalen Stelle! Nichts anderes macht doch DI aus, oder?
Und die zweite Frage, woher bekommt der Service seine Nutz-Daten, also die Entity-objekte? Auch hier wäre meine Antwort von einer globalen Stelle!
Ein weiterer Service wird dann später vermutlich diesen Service hier verwenden.
Bei einem Spezialfall des Bannes wird er vermutlich direkt "Unterprozeduren" verwenden müssen, um Logik an der entsprechenden Stelle gezielt ersetzen zu können! Also Logik die nicht durch einfache DI auflösbar wäre!
Ich bin mir nicht ganz sicher ob du verstanden hast, was ich gemeint habe. Eventuell kannst du hier deine Aussage noch etwas weiter Ausführen
Was ist dann zwangsläufig OO, das würde mich interessieren^^
Zur Rekursion bzw. Divide and conquer komme ich später noch
und wie ich dezentrale und zentrale algorithmen sehe
Wie machst du es also in OOP?
Parameter stehen für mich für Flexibilität und Dynamik. Beides wird durch oo eher eingeschränkt, oder wie kann ich genauso dynamisch und vor allem flexibel in oo sein
Ein konkretes Beispiel, das am besten eine Transformation von prozedur(lange paremterliste){sub_routinen_aufrufe, etc} ins oo zeigt, würde mich mal interessieren
Fachlich ist sogar mein Assemblercode bzw. VHDL korrekt und semantisch tut es auch das gleiche. Man macht es aber trotzdem nicht, weil man anders besser bzw. effizienter im Sinne von man kommt schneller zum Ziel coden kann.
Jetzt würde mich auch interessieren ob man auch zum Ziel kommt, wenn man derartige Ausnahmen auslässt und wirklich reines OO programmiert? Und vor allem interessiert mich wie dieses OO dann aussehen muss
Naja die Frage ist hier doch viel mehr ob zentralisierter Code prozedural ist und dezentralisierter eher OO?
Und die zweite Frage ist, wenn kein globaler Kontext, wie ist dann der globale Kontext zu dividen? Und wie komme ich dann an die divideden (aufgeteilten) Informationen?
Ja diese Profitierung würde mich auch interessieren
Naja wie ihr selber schon gesagt habt ist OO ne Erweiterung von POP. Die Probleme lösen sich also nicht durch POP auf, sondern exisitieren hier noch gar nicht. Und ich meine hier insbesondere Datenstrukturkopplungen, Wo welches Verhalten hin? dezentralen Algorithmus (der meiner Meinung nach nur in sehr wenigen Fällen das gelbe vom Ei ist.... später dazu mehr)
sehr interessanter blog eintrag!
Eigentlich spiegelt er auch meine zwei ansichten wieder, oder?
Also erstere, dass ban_user weder zu user noch zu admin gehören kann -> man muss eine Service-Klasse einführen!
Zweitere: 1D-Ansicht ist gut MehrD nicht!
Heißt er findet den ADT (abstrakten Datentyp) einer Liste gut umgsetzt. Die Operationen arbeiten ja auch wirklich nur auf den eigenen Daten. Den Rest sieht er kritisch (also sonstige Zusammenfassungen)
Stimmt das in etwa so? Hab den Text vor ein paar Tagen durchgelesen und jetzt nicht mehr wiederholt.....
2. Weitere Ausführungen oo vs pop
Also pop kann man zentralisiert wie auch dezentralisiert programmieren.
OO steht eher für dezentral
Stimmt das in etwa so?
Zentral heißt ich habe eine prozedur, die alles steuert bzw. ein Service-Objekt (proezduraler Ansatz) das alles steuert
Dezentral heißst ich habe eine prozedur die leitet die Anfrage weiter und soweiter und am ende wird gemäß des Divide and Conquer wieder alles gemerged?
Hießt ich code Klassen mit Baisfällen und Merge-Klassen....?
Kann man das so ungefähr sehen, bzw. ist es das, was OO ausmacht?
Den Voteil von zentral sehe ich darin, dass, ich Teilergebnisse im Algorithmus wiederverwenden kann und auch in abhängigkeit von den Teilergebnissen den Algorithmus steuern kann
siehe dazu (wobei das sogar ne Mischform ist)
->Aufruf->Aufruf->Aufruf
->Aufruf
->Aufruf
->Aufruf->Aufruf->Aufruf
->Aufruf
->Aufruf
Beim Divide and Conquer Algorithmus kann ich Teilergebnisse aus einem Rekursionspfad nicht im anderen verwenden! Zumindest nicht trivial. Über eine globale Konstruktion wäre dies möglich!
welche Vorteile bringt es dezentral zu programmieren?
Und ist dezentral reines oo und zentral pop und eine Mischform aus beidem praktisches oo (wobei in der Mischform das zentrale dann der Service darstellt?)
Soweit hierzu
3. Definition von pop und oo
pop: ich habe eine prozedur, diese verwendet weitere Funktionen (vor allem aber auch Sequentiell, also in zentraler Form)
Die Daten bekomme ich über parameter, eventuell auch zu entity-objects oder arrays zusammengefasst
persistente Daten werden via eines globalen Layers in die Datenbank eingetragen bzw. gelesen. Dieser Layer ist von überall aus Zugreifbar
oo:
Kapselung von verhalten und Daten
dezentraler Algorithmus?
wie komme ich hier an die Daten, bzw. wo sind die gespeichert?... diese Struktur wird doch auch von was globalen aufgebaut oder?
4. Was mich an oo stört (Zusammenfassung)
Datenstrukturkopplung wie schon in den anderen Thread erwähnt
Unentschlossenheit wo ich welches Verhalten hindesigne
reiner dezentraler Algorithmus, da hier Flexibilitätseinbußen, weil mir hier nicht alle Teilergebnisse überall zur Verfügung stehen (Hier meine ich wirklich Teilergebnisse, also Produkte, die während des algorithmus anfallen)
5. Fallbeispiel
Also wenn ich ne Anwendung entwerfe, dann sieht das folgendermaßen aus
In der Regel hab ich einen Use-Case bzw. ne Anforderung an eine Funktion/Prozedur
Diese schreibe ich komplett aus... -> 1 Funktion/prozedur (die beiden begriffe verwende ich im folgenden synonym)
dann geh ich so vor, ich zerkleinere die Funktion in kleinere Teilabschnitte.... Diese kann ich ja dann später super wiederverwenden, wenn ich nur an einem Teil-feature dieser Anforderung interessiert bin
Natürlich gehe ich nicht immer so vor, sondern schreibe auch mal erst ne Sub-funktion etc (also middle-out), oder sogar bottom-up.
Jede einzelne Funktion, die ich geschrieben habe ist super wiederverwendbar und ich hab nix umsonst geschrieben
Um konkreter zu werden.... gehe ich nochmal auf das ban_user problem ein
ich schreibe eine prozedur ban_user($to_user,$ban_user,$force_ban=false, array $weitere_flags)
diese checkt dann erst mal ob der $ban_user berechtigt ist zu bannen
diese checkt anschließend ob $ban_user berechtigt ist den zu ihm in bezug stehenden user zu bannen (also ob dieser im hirachisch unterlegen ist)
diese checkt ob $ban_user sein tageslimit an bans heute schon überschritten hat
und und und
bei force_ban wird diese checks umgangen
erst anschließend wird der ban durchgeführt, dabei müssen wieder x Daten an y-vielen Entitiies bzw. Daten geändert werden
Tritt hierbei nochmals eine Exception auf und ist diese nicht kritisch wird der ban weiter durchgezogen
ist die exception kritisch dann wird auch hier wiederrum ein rollback durchgeführt
In der Regel sollten keine Exceptions auftreten, da die checks dies abedecken sollten.... doppelt hält aber besser^^
Eventuell werden auch Emails versendet, einmal an den ban_user und and den $to_user.... in abhängigkeit von weitere_flags werden diese forciert oder aber die default einstellungen des users genommen, die über eine sub-prozedur abgefragt werden (also über eine wohldefinierte und abstrakte schnittstelle!)
Ich arbeite also auf globalen Daten, aber nur mit wohldefnierten schnittstellen (also via parameter)
So diese prozedur kann vom user verwendet werden, also ich gebs über den ActionControll-Mechanismus nach draussen
Oder aber ich möchte dieses Feature innerhalb verwenden.
Beispielsweise schreibt ein Admin ne Nachricht an den User dass er gebannt wird. Mein Texterkennungssystem erkennt dass es sich um eine derartige Nachricht handelt und ruft dann die prozedur ban_user(....) mit den entsprechenden Parametern auf!
Oder aber es ist nicht erlaubt, dass der User sein Name in etwas unArtiges ändert, auch bei dieser Änderung wird diese Prozedur aufgerufen!
Wichtig ist hierbei zu wissen, dass ich des möglichst flexibel gestalten möchte. also diesen Aufruf aus changeName() aufzurufen wäre falsch, DA:
Wenn der Name vom System aus, oder von einem dazu berechtigten Admin in etwas UnArtiges geändert wird, dann soll ban_user nicht aufgerufen werden, also:
changeNameSpecial()-> changeName() //wobei diese funktion dem system und dem ber. Admin vorenthalten ist
changeNameNormal()->changeName() und eventuell ->ban_user() //diese wiederum nur dem User, oder aber auch dem System, wenn man will, dass eventuell ein UserBan durchgeführt wird, wenn das System auch einen Unartigen Namen vergibt
wie ihr seht bin ich sehr auf flexibilität aus!
Bin mal gespannt wie ihr das mit richtigen und wirklich reinem OO besser hinbekommt......
6. GoF und Sonstiges
Hier jetzt mal ganz nüchtern die Frage an euch, wie stellt ihr euch OOA und dann vor allem OOD (oo-Design) vor und zwar nicht nur auf den Problembereich bezogen, sondern auch auf den Lösungsbereich bezogen?
Also alle "technischen Klassen" (sowas wie Services oder etc) mit einbezogen
Wobei wie wir ja oben (siehe Fowlers Beitrag) gesehen haben, sind Services ja eigentlich nicht zu verwenden!
Welche Klassenrollen verwendet ihr
würde mich über eine Aufzählung und ne kurze Erläuterung freuen
bspw.
User-Entity
USer-Service
User-UseCase
User-Action
also sowas in der Art und dann auch die semantische Bedeutung davon und vor allem die feingranularen Unterschiede
Gibt ja für Geschäftsprozesse und UseCases auch kleine und feine Unterschiede
Ich kenne mich da natürlich nicht so ganz gut aus, weiß bloss, dass es hier Unterschiede gibt
Dann noch die Frage sind einzelene Rollen-Klassen als Layer zu sehen?
Bspw. geht man bei nem Service von nem Service-Layer aus, dann hätte ich des Problem, dass ich später von einer Entity-Klasse keinen Service aufrufen dürfte, da sonst das strenge Schichtenmodell verletzt werden würde?!?
Wäre aber doof, weil ich ja bspw. bei nem changeName (mit flexibilitätseinbußen) ja bspw. einen Service aufrufen möchte (also für mich klingt Service nach wie vor so, wie wenn es eine Prozedur wäre, auch dann wenn Strategien eingespritzt werden, um den Algorithmus zu konkretisieren.... siehe Codebeispiel von rkr)
Die GoF hat ja so schöne Design-Patterns entworfen. Wenn ich in Rollen und Layern code, wo kommen diese Patterns dann zum tragen? Vermutlich hauptsächlich in den Service-Klassen? (also die Verhaltensbasierten Patterns)???
Noch ne weitere Frage zum Single-Responsibility-Prinzip:
Wenn ich eine Entity-Klasse habe, dann ist die einzige Verantwortung hier, alle Entity-Attribute zu halten.... richtig?
Wenn ich eine Service-Klasse habe, dann ist die einzige Verantwortung hier, das Verhalten ausführen zu können.... richtig?
Wenn ich eine dezentrale Objektstruktur habe, dann ist die Verantwortlichkeit der Basisklassen eine einzige Verantwortlichkeit zu haben und die Aufgabe bzw. die Verantwortlichkeit der Merge-Klassen, die Ergebnisse von den rekursiv tieferen Klassen zu mergen und zu verarbeiten,....richtig?
Dazu noch eine weitere Frage:
Welchen Sinn macht es für eine Klasse mehr als eine öffentliche Methode zur Verfügung zu stellen? Welchen praktischen Sinn macht dies? Und verletzt es das Single-Responsible Prinzip? Ich würde sagen JA!
An der Stelle möchte ich noch kurz was zur Datenstrukturkopplung ergänzen:
Möchte ich eine Aufgabe iterativ mehrmals erledigen, dann hab ich in der oo ein großes Problem
Konkret:
ich habe bspw. ne Tabelle voller Geburtsdaten (diese ist extern.... Speise ich also extern in das System ein).... Diese Daten gehören semantisch zum User!
Möchte ich jetzt also bspw. das Durschnittsalter berechnen, dann muss ich ja über alle User iterieren und mir davon dann das Durchschnittsalter bilden
Problematik hier ist einerseits, ich bin an den User gebunden, also an die komplette Datenstruktur vom User und andereseits ein ein hochwertiges Objekt, das via new erzeugt werden muss
Zerstückle ich den User bspw. in Subklassen (eine bspw. die fürs alter zuständig ist), dann bekomme ich zumindest die Datenstrukturkopplung einigermaßen in den Griff
Zweiteres aber nicht! ich muss jedes mal ein Objekt, welcher Klasse dann auch immer erzeugen, dann die Geburtsdaten hineingeben und dann darüber iterieren!
Jedes mal new aufzurufen macht ein haufen Overhead!.... einfacher wäre es wenn ich direkt mit den geburtsdaten arbeiten würde
Ok ich merke gerade, dass das Beispiel eher ungeignet ist, weil ich das Verhalten eh externalisiert habe.
Ein besseres Beispiel wäre gewesen, wenn ich ein internes Verhalten gehabt hätte und dieses iterativ jedes mal hätte aufrufen müssen!
Also bspw. statt den Algorithmus komplett im Service zu haben, übernimmt bspw. der User noch das Geburtsdatum in eine Zahl umzuwandeln, oder so!
Aber ich denke meine Kernproblematik hier wird klar.... Ich muss jedes mal new aufrufen statt einfach mit einer prozedur zu arbeiten , die die Daten ganz einfach via Parameter übergeben bekommt
Die Methode ist also auch an die Klassen eigenen Attribute gekapselt, was bei einem iterativen Darüberlaufen und "einfachen" Daten einen haufen Overhead erzeugt!
Vlt. wird es durch folgendes noch etwas klarer:
Ich denke damit ist meine Problematik klarer geworden
Wäre A eine noch etwas aufgeblähtere Klasse, müssten eventuell im Konstrukor noch weitere Daten gesetzt werden, die aber in do_something() gar nicht benötig werden würden
7. Schlusswort
Nein ich möchte damit nur meine Bedenken äußern bzw. aufzeigen, dass vieles auch in prozeduraler Programmierung möglich ist!
Bzw. wie ihr ja selbst gesagt habt, ist OO nur eine Erweiterung zu Prozeduraler programmierung
Ich zeige damit nur, dass Dinge, die ihr öfters als alleiniges Merkmal der oo zuschreibt, auch in pop möglich ist (wie z.B Polymorphie)
Was ihr aber auch sagt ist, dass oo einfach ein anderes Denkmuster ist, und genau um das geht es mir. Ist es das dezentrale Denkmuster (divide and conquer)?
Wenn ja wie sieht es praktisch aus?
Welche Rollen-Klassen gibt es.
Wie sind die anzuwenden?
Wo kommen die Daten her?
Wie greife ich auf Daten von aggregierten Strukturen zu. Gebe ich Subobjekte zurück, oder "repgorammiere" ich die Schnittstelle zu tieferen Objekten, da ja eigentlich die Internas und damit auch die Sub-Objekte verdeckt werden sollten (information-hiding)????
Wenn Daten nicht global sind, wie komme ich dann an diese Daten in einer bestimmten methode?, Wie werden die Daten verteilt?
Naja solche Sachen interessieren mich.... oben in meinem Text habe ich ja einige geannant, würde mich über Antworten freuen!
Du darfst mir gerne ein Best Practivse von OOA und OOD geben! Würde mich echt interessieren wie du so ne Anwendung entwirfst. Aber nicht nur leeren Code, sondern auch kommentierten Code, in dem du beschreibst, warum du folgende Design-Entscheidung getroffen hast und warum diese eventuell besser ist als eine andere..... also ein ausdifferenziertes Beispiel
Ich denke ich habe sicher noch irgendwelche Gesichtspunkte vergessen
Werde, wenn ich mich an diese erinnere, bei Gelegenheit nachliefern.
Übrigens das dezentrale Währungsystem Bitcoin kratzt gerade eben an der 210 USD Marke. Eine wichtige Marke.... Naja 220USD war eigentlich wichtiger, da diese durchbrochen worden ist..... naja wir werden sehen!
Schauen wir mal ob das dezentrale System überlebt
Ich denke das ist ein guter Abschluss^^
lg knotenpunkt
So diesesmal ein etwas kürzerer Beitrag
Gliederung:
0. Allgemeines
1. Zitate
2. Weitere Ausführungen oo vs pop
3. Definition von pop und oo
4. Was mich an oo stört (Zusammenfassung)
5. Fallbeispiel
6. GoF und Sonstiges
7. Schlusswort
0. Allgemeines:
Da mir jetzt schon öfters aufgefallen ist, dass ihr einen Bezug zu PHP herstellt.
Ich möchte eher allgemeine Paradigmen von oo und pop besprechen und nicht irgendwelche auf irgendeine Sprache bezogene Paradigmen
Vlt. erkennt ihr auch daran mein Denkmuster. Möglichst generisch und flexibel programmieren. Und genau da macht mir oo irgendwie einen Strich durch die Rechnung (vermutlich)
1 Zitate:
In einen Service. Ein User kann sich nicht selbst Bannen. Ein User hat eigentlich kein richtiges Verhalten. Du kannst dem User aber Verhalten geben: Durch Erweiterungs-Klassen.
Das Prinzip von Erweiterungsklassen hab ich hier aber nicht wirklich verstanden, welchen Mehrwert hab ich denn hier?
Wenn ich jetzt ausgehend vom User wissen möchte ob er gebannt ist, dann muss ich die Erweiterungsklasse derefernzieren, ok! Spinnt man das ganze aber weiter, dann könnte es am Ende so aussehen, dass ich ne class user hab mit nix drinnen und nur Erweiterungsklassen draussen herum baue?!?
Und da schon die nächste Frage, woher bekommt der UserBanCtrl die Referenz auf diese Erweiterungsklasse. In deinem Beispiel von der Factory ganz frisch. Aber normalerweise wohl doch dann von einer globalen Stelle, einer Registry oder global von der Datenbank, oder?
Würde mich über weitere Ausführungen hier freuen....
Ich sehe in meiner Implementation immerhin den Unterschied, dem BanUserCtrl für BannableFactory etwas zu geben, was die "Bann-Regeln" anders interpretiert oder eine andere Datenquelle zur Hilfe nimmt. Wie mache ich so was elegant prozedural?
Dazu muss ich nochmal etwas ausholen.
Assembler: indirekte Addressierung:
lese ich einen indirekten wert bspw. aus einem register (äh quatsch ist glaube ich sogar semantisch verboten aus nem register indirekt zu dereferenzieren)..... also aus ner Speicherstelle, dann ist der Wert den ich lese die Referenz auf eine weitere Speicherstelle und lese diesen dann!
entspricht diese Speicherstelle nun einer Funktion, so kann ich während des Laufen des programms an der ursprünglichen Stelle, also an der Stelle die dann die Dereferenzierung angibt einen anderen Wert eintragen, der dann dafür sorgt, dass eine andere Funktion angesprungen wird.
In PHP funktioniert das glaube ich mittels call_user_func oder so.....
in C wird ein Funktionspointer dereferenziert
Und in der OO-Programmierung ist es die polymorphie, die bspw. mittels interfaces umgesetzt wird
=> Machen aber alle vier semantisch das gleiche!
Um bspw. später if else zeugs zu vermeiden, setze ich einmalig (bzw. später auch änderbar) so einen Funktionspointer.
In obigen Beispiel gibt der dann die "Bann-Rgeln"-Strategie vor!
Die Frage ist nur, WO ich diesen Funktionspointer abspeichere?
An einer globalen Stelle (oo und pop möglich), oder lokal (nur oo möglich)
Und hier muss ich dir tatsächlich recht geben, dass es wohl strukturierter Ist das Verhalten direkt im objekt abzuspeichern.
Aber das ist hier auch das einzigste was OO "besser" kann als pop.
Naja hier kommt ein klein wenig Zustand dazu, was aus dem Service ohne Zustand einen Service mit etwas Zustand macht.
Aber die Kernproblematik besteht hier weiter, wir verwenden einen Service, der eher global agiert!
Und für mich stellt sich die weitere Frage, von woher bekommt der Service seine Strategien? Meine Antwort darauf wäre von einer globalen Stelle! Nichts anderes macht doch DI aus, oder?
Und die zweite Frage, woher bekommt der Service seine Nutz-Daten, also die Entity-objekte? Auch hier wäre meine Antwort von einer globalen Stelle!
Ein weiterer Service wird dann später vermutlich diesen Service hier verwenden.
Bei einem Spezialfall des Bannes wird er vermutlich direkt "Unterprozeduren" verwenden müssen, um Logik an der entsprechenden Stelle gezielt ersetzen zu können! Also Logik die nicht durch einfache DI auflösbar wäre!
Der typischste Fall für ein mehrdimensionales Objektdesign ist wohl eine Baumstruktur. Also ein DOM, eine Verzeichnisstruktur, oder eine Window-Component-Lib. Es gibt gute Gründe, warum man das eher nicht prozedural macht. OpenGL war lange Prozedural. DirectX war schon immer (auch) Objektorientiert. Ein wunder: Die überwiegende Zahl der Entwickler nutzt DirectX gerne und OpenGL nicht.
Man kann alles als OO durchgehen lassen, was OO ist. Sub-Methoden-Aufrufe sind nicht zwangsläufig OO, nur weil sie in einem Objekt stattfinden. Das kann ganz normale prozedurale Verkettung sein. Hab ich keine Schmerzen bei, wenn es das Problem fachlich korrekt, bzw. ideal löst.
Mehrdimensionalität ist dann die sogenannte Rekursion. Und das ist ebenfalls korrektes OO.
und wie ich dezentrale und zentrale algorithmen sehe
Wie kann ich diese einzelnen Concerns prozedural aufteilen, so dass ich jeweils klar abgetrennte Subsysteme habe, die sich um die einzelnen Belange kümmern? Zumal diese Subsysteme dann wahrscheinlich wieder Abhängigkeiten zu anderen Komponenten haben, die dann die Anbindung zu einer Datenquelle stellen, etc. Das mündet bei prozeduraler Programmierung nur wieder in übergreifenden Funktionsaufrufen mit kreuz- und quer-referenzierten oder -kopierten Structs. Das ist von der herangehensweise ganz anders als OOP. Für einige kleine Belange mag das so besser funktionieren. Aber für komplexe Geschäftsanwendungen mit viel Code ist das einfach nichts. Und OOP eignet sich eigentlich auch für alles, was man mit prozeduralen Paradigmen erledigen kann. Warum also nicht gleich OOP?
Parameter stehen für mich für Flexibilität und Dynamik. Beides wird durch oo eher eingeschränkt, oder wie kann ich genauso dynamisch und vor allem flexibel in oo sein
Ein konkretes Beispiel, das am besten eine Transformation von prozedur(lange paremterliste){sub_routinen_aufrufe, etc} ins oo zeigt, würde mich mal interessieren
Man kann alles als OO durchgehen lassen, was OO ist. Sub-Methoden-Aufrufe sind nicht zwangsläufig OO, nur weil sie in einem Objekt stattfinden. Das kann ganz normale prozedurale Verkettung sein. Hab ich keine Schmerzen bei, wenn es das Problem fachlich korrekt, bzw. ideal löst.
Jetzt würde mich auch interessieren ob man auch zum Ziel kommt, wenn man derartige Ausnahmen auslässt und wirklich reines OO programmiert? Und vor allem interessiert mich wie dieses OO dann aussehen muss
Ja, falsch. Das geht bei OOP ebenfalls genau so. Absolut kein Unterschied zu deiner Variante, bis auf, dass es diesen "globalen" Kontext nicht geben muss. Oder anders ausgedrückt: Die OOP-Experten mit dem schwarzen Gurt nutzen keine globalen Informationen und keine globalen Objekte(Singleton)/Methoden(Statisch), sehr wohl aber in Objekten verschachtelte prozedurale Ansätze.
Und die zweite Frage ist, wenn kein globaler Kontext, wie ist dann der globale Kontext zu dividen? Und wie komme ich dann an die divideden (aufgeteilten) Informationen?
Dann profitiert man eben nur unvollständig. Was auch immer das bedeuten soll. Klingt jedenfalls besser, als gar nicht zu profitieren.
Erklär du uns doch vielleicht umgekehrt mal anschaulich, wieso sich bei prozeduraler Programmierung immer alles – auch die Probleme von OOP, die du feststellst – in Wohlgefallen auflöst.)
Auch das was ein M. Fowler schreibt, kann und sollte man kritisch betrachten:
http://blog.ralfw.de/2010/08/leiden-...-oop-2010.html
http://blog.ralfw.de/2010/08/leiden-...-oop-2010.html
Eigentlich spiegelt er auch meine zwei ansichten wieder, oder?
Also erstere, dass ban_user weder zu user noch zu admin gehören kann -> man muss eine Service-Klasse einführen!
Zweitere: 1D-Ansicht ist gut MehrD nicht!
Heißt er findet den ADT (abstrakten Datentyp) einer Liste gut umgsetzt. Die Operationen arbeiten ja auch wirklich nur auf den eigenen Daten. Den Rest sieht er kritisch (also sonstige Zusammenfassungen)
Stimmt das in etwa so? Hab den Text vor ein paar Tagen durchgelesen und jetzt nicht mehr wiederholt.....
2. Weitere Ausführungen oo vs pop
Also pop kann man zentralisiert wie auch dezentralisiert programmieren.
OO steht eher für dezentral
Stimmt das in etwa so?
Zentral heißt ich habe eine prozedur, die alles steuert bzw. ein Service-Objekt (proezduraler Ansatz) das alles steuert
Dezentral heißst ich habe eine prozedur die leitet die Anfrage weiter und soweiter und am ende wird gemäß des Divide and Conquer wieder alles gemerged?
Hießt ich code Klassen mit Baisfällen und Merge-Klassen....?
Kann man das so ungefähr sehen, bzw. ist es das, was OO ausmacht?
Den Voteil von zentral sehe ich darin, dass, ich Teilergebnisse im Algorithmus wiederverwenden kann und auch in abhängigkeit von den Teilergebnissen den Algorithmus steuern kann
siehe dazu (wobei das sogar ne Mischform ist)
->Aufruf->Aufruf->Aufruf
->Aufruf
->Aufruf
->Aufruf->Aufruf->Aufruf
->Aufruf
->Aufruf
Beim Divide and Conquer Algorithmus kann ich Teilergebnisse aus einem Rekursionspfad nicht im anderen verwenden! Zumindest nicht trivial. Über eine globale Konstruktion wäre dies möglich!
welche Vorteile bringt es dezentral zu programmieren?
Und ist dezentral reines oo und zentral pop und eine Mischform aus beidem praktisches oo (wobei in der Mischform das zentrale dann der Service darstellt?)
Soweit hierzu
3. Definition von pop und oo
pop: ich habe eine prozedur, diese verwendet weitere Funktionen (vor allem aber auch Sequentiell, also in zentraler Form)
Die Daten bekomme ich über parameter, eventuell auch zu entity-objects oder arrays zusammengefasst
persistente Daten werden via eines globalen Layers in die Datenbank eingetragen bzw. gelesen. Dieser Layer ist von überall aus Zugreifbar
oo:
Kapselung von verhalten und Daten
dezentraler Algorithmus?
wie komme ich hier an die Daten, bzw. wo sind die gespeichert?... diese Struktur wird doch auch von was globalen aufgebaut oder?
4. Was mich an oo stört (Zusammenfassung)
Datenstrukturkopplung wie schon in den anderen Thread erwähnt
Unentschlossenheit wo ich welches Verhalten hindesigne
reiner dezentraler Algorithmus, da hier Flexibilitätseinbußen, weil mir hier nicht alle Teilergebnisse überall zur Verfügung stehen (Hier meine ich wirklich Teilergebnisse, also Produkte, die während des algorithmus anfallen)
5. Fallbeispiel
Also wenn ich ne Anwendung entwerfe, dann sieht das folgendermaßen aus
In der Regel hab ich einen Use-Case bzw. ne Anforderung an eine Funktion/Prozedur
Diese schreibe ich komplett aus... -> 1 Funktion/prozedur (die beiden begriffe verwende ich im folgenden synonym)
dann geh ich so vor, ich zerkleinere die Funktion in kleinere Teilabschnitte.... Diese kann ich ja dann später super wiederverwenden, wenn ich nur an einem Teil-feature dieser Anforderung interessiert bin
Natürlich gehe ich nicht immer so vor, sondern schreibe auch mal erst ne Sub-funktion etc (also middle-out), oder sogar bottom-up.
Jede einzelne Funktion, die ich geschrieben habe ist super wiederverwendbar und ich hab nix umsonst geschrieben
Um konkreter zu werden.... gehe ich nochmal auf das ban_user problem ein
ich schreibe eine prozedur ban_user($to_user,$ban_user,$force_ban=false, array $weitere_flags)
diese checkt dann erst mal ob der $ban_user berechtigt ist zu bannen
diese checkt anschließend ob $ban_user berechtigt ist den zu ihm in bezug stehenden user zu bannen (also ob dieser im hirachisch unterlegen ist)
diese checkt ob $ban_user sein tageslimit an bans heute schon überschritten hat
und und und
bei force_ban wird diese checks umgangen
erst anschließend wird der ban durchgeführt, dabei müssen wieder x Daten an y-vielen Entitiies bzw. Daten geändert werden
Tritt hierbei nochmals eine Exception auf und ist diese nicht kritisch wird der ban weiter durchgezogen
ist die exception kritisch dann wird auch hier wiederrum ein rollback durchgeführt
In der Regel sollten keine Exceptions auftreten, da die checks dies abedecken sollten.... doppelt hält aber besser^^
Eventuell werden auch Emails versendet, einmal an den ban_user und and den $to_user.... in abhängigkeit von weitere_flags werden diese forciert oder aber die default einstellungen des users genommen, die über eine sub-prozedur abgefragt werden (also über eine wohldefinierte und abstrakte schnittstelle!)
Ich arbeite also auf globalen Daten, aber nur mit wohldefnierten schnittstellen (also via parameter)
So diese prozedur kann vom user verwendet werden, also ich gebs über den ActionControll-Mechanismus nach draussen
Oder aber ich möchte dieses Feature innerhalb verwenden.
Beispielsweise schreibt ein Admin ne Nachricht an den User dass er gebannt wird. Mein Texterkennungssystem erkennt dass es sich um eine derartige Nachricht handelt und ruft dann die prozedur ban_user(....) mit den entsprechenden Parametern auf!
Oder aber es ist nicht erlaubt, dass der User sein Name in etwas unArtiges ändert, auch bei dieser Änderung wird diese Prozedur aufgerufen!
Wichtig ist hierbei zu wissen, dass ich des möglichst flexibel gestalten möchte. also diesen Aufruf aus changeName() aufzurufen wäre falsch, DA:
Wenn der Name vom System aus, oder von einem dazu berechtigten Admin in etwas UnArtiges geändert wird, dann soll ban_user nicht aufgerufen werden, also:
changeNameSpecial()-> changeName() //wobei diese funktion dem system und dem ber. Admin vorenthalten ist
changeNameNormal()->changeName() und eventuell ->ban_user() //diese wiederum nur dem User, oder aber auch dem System, wenn man will, dass eventuell ein UserBan durchgeführt wird, wenn das System auch einen Unartigen Namen vergibt
wie ihr seht bin ich sehr auf flexibilität aus!
Bin mal gespannt wie ihr das mit richtigen und wirklich reinem OO besser hinbekommt......
6. GoF und Sonstiges
Hier jetzt mal ganz nüchtern die Frage an euch, wie stellt ihr euch OOA und dann vor allem OOD (oo-Design) vor und zwar nicht nur auf den Problembereich bezogen, sondern auch auf den Lösungsbereich bezogen?
Also alle "technischen Klassen" (sowas wie Services oder etc) mit einbezogen
Wobei wie wir ja oben (siehe Fowlers Beitrag) gesehen haben, sind Services ja eigentlich nicht zu verwenden!
Welche Klassenrollen verwendet ihr
würde mich über eine Aufzählung und ne kurze Erläuterung freuen
bspw.
User-Entity
USer-Service
User-UseCase
User-Action
also sowas in der Art und dann auch die semantische Bedeutung davon und vor allem die feingranularen Unterschiede
Gibt ja für Geschäftsprozesse und UseCases auch kleine und feine Unterschiede
Ich kenne mich da natürlich nicht so ganz gut aus, weiß bloss, dass es hier Unterschiede gibt
Dann noch die Frage sind einzelene Rollen-Klassen als Layer zu sehen?
Bspw. geht man bei nem Service von nem Service-Layer aus, dann hätte ich des Problem, dass ich später von einer Entity-Klasse keinen Service aufrufen dürfte, da sonst das strenge Schichtenmodell verletzt werden würde?!?
Wäre aber doof, weil ich ja bspw. bei nem changeName (mit flexibilitätseinbußen) ja bspw. einen Service aufrufen möchte (also für mich klingt Service nach wie vor so, wie wenn es eine Prozedur wäre, auch dann wenn Strategien eingespritzt werden, um den Algorithmus zu konkretisieren.... siehe Codebeispiel von rkr)
Die GoF hat ja so schöne Design-Patterns entworfen. Wenn ich in Rollen und Layern code, wo kommen diese Patterns dann zum tragen? Vermutlich hauptsächlich in den Service-Klassen? (also die Verhaltensbasierten Patterns)???
Noch ne weitere Frage zum Single-Responsibility-Prinzip:
Wenn ich eine Entity-Klasse habe, dann ist die einzige Verantwortung hier, alle Entity-Attribute zu halten.... richtig?
Wenn ich eine Service-Klasse habe, dann ist die einzige Verantwortung hier, das Verhalten ausführen zu können.... richtig?
Wenn ich eine dezentrale Objektstruktur habe, dann ist die Verantwortlichkeit der Basisklassen eine einzige Verantwortlichkeit zu haben und die Aufgabe bzw. die Verantwortlichkeit der Merge-Klassen, die Ergebnisse von den rekursiv tieferen Klassen zu mergen und zu verarbeiten,....richtig?
Dazu noch eine weitere Frage:
Welchen Sinn macht es für eine Klasse mehr als eine öffentliche Methode zur Verfügung zu stellen? Welchen praktischen Sinn macht dies? Und verletzt es das Single-Responsible Prinzip? Ich würde sagen JA!
An der Stelle möchte ich noch kurz was zur Datenstrukturkopplung ergänzen:
Möchte ich eine Aufgabe iterativ mehrmals erledigen, dann hab ich in der oo ein großes Problem
Konkret:
ich habe bspw. ne Tabelle voller Geburtsdaten (diese ist extern.... Speise ich also extern in das System ein).... Diese Daten gehören semantisch zum User!
Möchte ich jetzt also bspw. das Durschnittsalter berechnen, dann muss ich ja über alle User iterieren und mir davon dann das Durchschnittsalter bilden
Problematik hier ist einerseits, ich bin an den User gebunden, also an die komplette Datenstruktur vom User und andereseits ein ein hochwertiges Objekt, das via new erzeugt werden muss
Zerstückle ich den User bspw. in Subklassen (eine bspw. die fürs alter zuständig ist), dann bekomme ich zumindest die Datenstrukturkopplung einigermaßen in den Griff
Zweiteres aber nicht! ich muss jedes mal ein Objekt, welcher Klasse dann auch immer erzeugen, dann die Geburtsdaten hineingeben und dann darüber iterieren!
Jedes mal new aufzurufen macht ein haufen Overhead!.... einfacher wäre es wenn ich direkt mit den geburtsdaten arbeiten würde
Ok ich merke gerade, dass das Beispiel eher ungeignet ist, weil ich das Verhalten eh externalisiert habe.
Ein besseres Beispiel wäre gewesen, wenn ich ein internes Verhalten gehabt hätte und dieses iterativ jedes mal hätte aufrufen müssen!
Also bspw. statt den Algorithmus komplett im Service zu haben, übernimmt bspw. der User noch das Geburtsdatum in eine Zahl umzuwandeln, oder so!
Aber ich denke meine Kernproblematik hier wird klar.... Ich muss jedes mal new aufrufen statt einfach mit einer prozedur zu arbeiten , die die Daten ganz einfach via Parameter übergeben bekommt
Die Methode ist also auch an die Klassen eigenen Attribute gekapselt, was bei einem iterativen Darüberlaufen und "einfachen" Daten einen haufen Overhead erzeugt!
Vlt. wird es durch folgendes noch etwas klarer:
PHP-Code:
class A
{
$daten x
function do_something($daten_y){..../*mache was mit $this->x und mit $y*/}
function setX($var x)
{
$this->x=$x;
}
}
//und später dann
foreach($datensatz)
{
$a=new A;
$a->setX($datensatz[relevante Daten]);
Ergebnisverarbeitung = $a->do_something(5);
}
//einfacher und effizienter wäre gewesen
function do_somethingStrich($x, $y){....}
foreach($datensatz)
{
Ergebnisverarbeitung = $do_somethingStrich($datensatz[relevante Daten],5);
}
Wäre A eine noch etwas aufgeblähtere Klasse, müssten eventuell im Konstrukor noch weitere Daten gesetzt werden, die aber in do_something() gar nicht benötig werden würden
7. Schlusswort
sondern denke, dass du irgendwie versuchst, um das Thema OO herumzukommen und die Argumente dafür so streust, dass du dir und anderen dann erfolgreich einreden kannst, dass OOP nichts dolles ist.
Bzw. wie ihr ja selbst gesagt habt, ist OO nur eine Erweiterung zu Prozeduraler programmierung
Ich zeige damit nur, dass Dinge, die ihr öfters als alleiniges Merkmal der oo zuschreibt, auch in pop möglich ist (wie z.B Polymorphie)
Was ihr aber auch sagt ist, dass oo einfach ein anderes Denkmuster ist, und genau um das geht es mir. Ist es das dezentrale Denkmuster (divide and conquer)?
Wenn ja wie sieht es praktisch aus?
Welche Rollen-Klassen gibt es.
Wie sind die anzuwenden?
Wo kommen die Daten her?
Wie greife ich auf Daten von aggregierten Strukturen zu. Gebe ich Subobjekte zurück, oder "repgorammiere" ich die Schnittstelle zu tieferen Objekten, da ja eigentlich die Internas und damit auch die Sub-Objekte verdeckt werden sollten (information-hiding)????
Wenn Daten nicht global sind, wie komme ich dann an diese Daten in einer bestimmten methode?, Wie werden die Daten verteilt?
Naja solche Sachen interessieren mich.... oben in meinem Text habe ich ja einige geannant, würde mich über Antworten freuen!
Du darfst mir gerne ein Best Practivse von OOA und OOD geben! Würde mich echt interessieren wie du so ne Anwendung entwirfst. Aber nicht nur leeren Code, sondern auch kommentierten Code, in dem du beschreibst, warum du folgende Design-Entscheidung getroffen hast und warum diese eventuell besser ist als eine andere..... also ein ausdifferenziertes Beispiel
Ich denke ich habe sicher noch irgendwelche Gesichtspunkte vergessen
Werde, wenn ich mich an diese erinnere, bei Gelegenheit nachliefern.
Übrigens das dezentrale Währungsystem Bitcoin kratzt gerade eben an der 210 USD Marke. Eine wichtige Marke.... Naja 220USD war eigentlich wichtiger, da diese durchbrochen worden ist..... naja wir werden sehen!
Schauen wir mal ob das dezentrale System überlebt
Ich denke das ist ein guter Abschluss^^
lg knotenpunkt
Kommentar