OO ist gescheitert

?

Ist OO (definiert nach Alan Key) gescheitert?

  1. JA

  2. NEIN

Das Ergebnis kann erst nach Abgabe einer Stimme betrachtet werden.

Diskutiere OO ist gescheitert im Softwareentwicklung Forum; Du kannst also rein prozedural ein "flexibel und generisch einsetzbares Programmgerüst" schreiben? Ich denke du stimmst mir zu, wenn ich bspw. auf...

  1. knotenpunkt
    knotenpunkt Neues Mitglied
    Ich denke du stimmst mir zu, wenn ich bspw. auf alle Daten von überall zugreifen kann, dass ich da sehr flexibel und dynamisch unterwegs bin. Klar, macht das den Code schwerer testbar, aber bei ordentlicher Strukturierung ist man auch hier sicher gut testbar und mit wenig Code unterwegs.

    Ich glaube das ist einfach eine subjektive Haltung dazu.
    Aber, auch gut strukturierter Prozeduraler Code ist sehr gut lesbar, wartbar und testbar.
    Warum nicht performance mit dazu nehmen, wenn man auf oo verzichtet?
    Und ich glaube, vor allem weniger Code ist viel eher wartbar und erweiterbar als überdimensionierter oo-code?!

    Das ist ein Teil was OO ausmacht.
    Ein weiterers Teil wäre Verhalten wäre: Verhalten und Daten zu einem Paket zusammengeschürt (und damit habe ich meine Probleme)


    So programmiere ich viel Code, also 5 Klassen um einen UseCase abzudecken. Das ganze ist dann zudem schlecht erweiterbar.
    Nein ich habe für die gleiche Anwendung dann keine 17 Prozeduren.
    Und möchte ich hier etwas erweitern, reichen schon 1-2 weitere Prozeduren aus.
    Ich code somit zielorientiert und nicht overheadorintiert

    Die ein oder andere Datenbank kann EXEC aufrufe^^
    in der prozeduralen Anwendung lade ich mir EXAKT die Daten, die ich brauche.
    BSP, ein zielgerichtete SELECT anweisung mit diversen JOINS
    OO und relational passen nicht zusammen.
    In der OO müsste ich ganze Tabellen laden, diese zu Graphen zusammenbauen. Meinetwegen via lazy-loading Daten nachladen etc. pp und anschließend traversieren.

    ich definiere mir meine Datenstrukturen. Wenn ich eine Datenbank verwende also die Datenentitäten mit entsprechenden Schlüsseln.

    So und jetzt je nach UseCase, den ich brauche, picke ich mir entsprechende zusammenaggregierte Daten aus der Datenbank heraus und arbeite damit. Dies kann von einer Prozedur/Service-Klasse oder auch mehreren erledigt werden. Verwende ich mehrere Prozeduren, baue ich das ganze somit Modular auf.

    ein BSP:

    ein User kauft sich auf meiner Webseite irgendwas:
    Die Hauptprozedur fragt deligierend dann erstmal bei meiner Warenwirtschaftsprozedur nach ob Artikel verfügbar.
    Wenn ja dann ruft diese die Rechnungsprozedur, Versandprozedur, Loggingproezdur, Warenwirtschaftsprozedur2 auf und erledigt damit den UseCase. Diese Proezduren haben unter Umständen selbst weitere Unterprozeduren.
    Die Daten aus der Datenbank, anderweitigen API-Quellen holen sich diese Proezeduren selbstständig.
    Im Falle dessen dass ich jetzt nur eine Datenbankanbindung habe und nicht auf externe APIs setze kann ich somit auch noch ganz toll das ganze transactional umsetzen..... also falls irgendwo was schief gehen sollte.

    Somit arbeite ich zwar bis zu einem gewissen Grad global auf den Daten, aber durch die modulare Trennung von Rechnungsprozeduren, Warenwirtschaf............. schränke ich den globalen Spielraum wieder etwas ein.
    Somit bin ich hochflexibel und auch nicht an irgendwelche Objektgraphen gebunden.


    Warum lässt sich dann eine Anwendung nicht 100%ig oo bauen?
    In welchen Fällen verwendest du selbst Services, in welchen Fällen nicht?
    Wie entscheidest du das von Anfang an. Vor allem wenn du dein System gut erweiterbar aufbauen möchtest.


    Könntest du hierzu mal ein Beispiel posten, wie du das bei vernünftigen OO lösen würdest?
    Ganz allgemein würde mich interessieren, wie du schlechtes umgesetztes OO von gutem OO abgrenzt




    Ein OO-Konstrukt sollte meiner Meinung nach etwas langlebigeres sein. Eine Prozedur arbeitet ja nur kurz einmal mit ihren übergebenen Daten.
    Methoden in diesem langlebigen Konstrukt, sind eingeschränkt, da sie nur mit der starren langlebigen Situation arbeiten sollten/dürfen. kurzlebige zusammenaggregierte Daten in einer Prozedur kann ich immer wieder frisch/neu zusammensetzen. Auch in Abhängigkeit des Algorithmuses. Also schön flexibel und dynamisch.
    Also ein weiterer Punkt PRO Prozedur bzw. PRO Daten und Verhalten gehören nicht zusammen, nicht auf langlebige Sicht.

    Zu zweiterem: Das setzt aber eine festverdrahtete in sich geschlossene Objektlandschaft vorraus.
    Ein Entwurf, der nicht auf alle Daten zugriff hat, wird unflexibel. Wie greifst du bspw. vom MotorradObjekt auf das Sonnenobjekt zu und zwar exakt auf die Sonneninnentemperatur, sodass dein MotorradObjekt irgendwas machen kann, sollte dieser Anwendungsfall von Anfang an noch nicht vorgehsehen sein.
    Um diesen Datenzugriff zu ermöglichen, musst du jetzt einiges erweitern.
    Eventuell private Daten der Sonne freigeben, seis über ein getSonnenInnenTemperatur()...... Zwischenobjekte, also sprich Objekte die im Objektgraphen zwischen Motorrad und Sonne liegen, müssen diese Information irgendwie weitergeben, etc. pp
    Das ist doch kein sinnvoller Entwurf mehr?!

    Ausserdem stellt sich dann auch noch die Frage, sollte für die MotorradMotrensteuerung nicht nur die SonnenInnenTemperatur von Interesse sein, sondern allerlei anderes auch noch, gehört das fachlich dann überhaupt in eine Objektmethode des Motorrads/Motorradsmotors? Oder ist hier nicht sogar ein Service sinnvoller?


    Er darf nie negativ werden, wenn ein KontoInhaber diese Transaction macht.
    Ein Admin darf das Konte des Users aber auch gerne mal ins Negative abrutschen lassen.
    Das Verhalten gehört meiner Meinung nach nicht zum Konto, sondern zum Anwendungsfall
    Ein klares Indiz dafür, dass Daten und Verhalten getrennt werden müssen.

    Oder das Konto darf nicht negativ werden, wenn es 7 Uhr abends ist, der User eine negativen Schufaeintrag hat und wenn der Milchbauer vom Nebenort mindestens 70% seiner Photovoltaikanlage in den letzten 2Wochen als Eigenverbrauch verwendet hat.


    Vllt. aber möchte ich diesen UseCase nicht von Anfang an haben, sondern wirklich erstmal Konto darf nicht negativ werden. Ich möchte aber trotzdem die Möglichkeit haben, es zu meinem erweiterten UseCase hingehend zu erweitern.
    Wie würdest du das machen?
    Auch unter dem Gesichtspunkt betrachtet, dass ein nerviges Entlanghageln des Objektgraphen nicht erwünscht ist. Wie komme ich zu den Informationen 7Uhr, 70%, Nachbar....... Ohne dass ich mich in einem komplexen Objektgraphen entlanghangeln muss?


    *****************************************************************
    Nein: OO != Polymorphie
    OO == Verhalten und Daten zusammengekapselt
    OO == Austausch via Messages
    und noch ein paar Kriterien

    Zum Thema polymorphie ist mir gerade noch ein weiteres OO Designproblem aufgefallen.
    Man nehme bspw. ein Dreieck (Klasse Dreieck)
    In der Schule wird einem gelehrt, dass bspw das Dreick am besten selbst weiß wie es sich zeichnen soll


    Die Polymorphie besteht in dem Fall darin, dass sich bei aufruf der draw()-Methode das geometrische Unterobjekt Dreieck anders selbst zeichnen wird, wie die draw()-Methode eines Vierecks.

    Problem: Wo soll sich das Dreieck hinzeichnen?: Auf ein Canvaselement bspw!
    Ist das aber wirklich die Aufgabe des Dreiecks, sich auf das Canvasobjekt zu zeichnen?
    Meiner Meinung nach gehört diese Funktionaltät nicht in die Dreiecksklasse.
    Sie gehört meiner Meinung nach in ein Modul, das für Zeichnen zuständig ist.

    Möchte ich das Dreieck wiederverwenden, in einem System, in dem rein gar nichts gezeichnet wird, ist ein Dreieck ohne derartiges Verhalten definitiv besser aufgehoben.
    Und das macht die Proezudurale programmierung auch so schön wiederverwendbar.
    Ich schmeisse entsprechende Prozeduren raus, wenn ich sie woanders nicht brauche, oder ich nehme welche dazu und erweitere das system so.



    @mrBrown

    Was mir immer noch nicht ganz klar ist:

    ich habe eine KlassenGraphStruktur, die wie folgt aussehen könnte.

    Code (Text):

    class X
    {

    K übergerordnetes Objekt; //also sprich hier ist eine Referenz drinnnen von dem Objekt, das X enthält

    T memVariable1 -> Y memVariable2 aus T -> memVariable3 aus Y
    usw

    methode1();
    methode2();
    methode3();

    }
     
    //ja klasse und klasseninstanz etwas vermischt, aber ich denke es wird klar, was ich meine

    1 Frage ) wie greife ich von methode1 auf irgendwas von memVariable3 zu?
    Eventuell: ich hangle mich im Objektgraphen entlang (aber du meintest, das wäre nicht sinnvoll)

    2 Frage )
    das ganze ist doch dann auch wieder global zu sehen, weil sich methode1 sich die Daten selbst holt
    Ich habe lediglich den Overhead des Graphenentlanghangelns drinnen.

    3 Frage )
    greift methode2 auf K zu, bzw. ganz allgemein die Existenz von K in X macht meinen Objektgraphen zyklisch
    Ist das sinnvoll in OO?

    4 Frage )
    Nehmen wir an X ist ein Konto:
    Es soll in dem Konto ein Betrag/Variable geändert werden.
    Es wird dazu memVariable3 aus Y benötigt, ABER auch in dem Objektgraphen nicht vorhanden, ein anderes Konto X...... das ich aber zur Compilezeit noch nicht kenne, sondern erst dynamisch ermitteln muss.
    Ganz generell, dieser Objektgraph wie oben angegeben ist ein ein starres Gebilde........ und mindestens aber Teil einer längerlebigen Struktur.

    Für viele UseCases brauche ich nur kurzlebige strukturen...... und da finde ich es quatsch einen Objektgraphen aufzubauen, DO_METHODE() ausführen...... Objektgraphen dem Destruktur/GC zuzuführen.

    Meine Idee wäre hier: Ich brauche mal wieder eine Prozedur, die das übergeordnet erledigt.
    Also habe ich wieder eine Service-Klasse, die aber kein reines OO darstellt.
    Wie würdest du das in reines OO umwandeln?

    5 Frage )
    Viele Use-Cases wollen keine starre Strukur, also kein vorgegebenen Objektgraphen, sondern entscheiden erst zur Laufzeit, welche Daten sie genau benötigen.
    Wie würdest du das OO umsetzen?



    Noch eine ganz allgemeine Frage:
    Wenn du deine Software iterativ aufbaust, am Anfang noch nicht genau weißt wie du was du haben möchtest, wie programmierst du da, bzw. wie gehst du da vor, wie ist dein Buildprozess.
    Das Problem sieht man ja am Konto mit dem negativen Kontostand. Wenn ich ein USeCase habe, den aber erst im laufe meines programmierens erarbeite, wie gehst du da vor?
    Aufwändige Refactorings sollten ausgeschlossen sein, da diese Vorgehensweise (der iterative, top-bottom.... Aufbau) zur Programmiermethodik gehören soll.
    Ich möchte eine Software so aufbauen, dass ich sie immer gut und schmerzfrei erweitern kann, das sehe ich irgendwie bei der OO nicht gegeben, bei PP dagegen schon.


    lg knotenpunkt
     
    Zuletzt von einem Moderator bearbeitet: 18. Mai 2018
  2. mrBrown
    mrBrown Super-Moderator Mitarbeiter
    Nein, wenn von überall alles verändert werden kann, hast du einen großen Haufen Scheiße produziert.

    Daran ist überhaupt nichts wartbar, strukturiert oder testbar.

    gut strukturierter Code ist lesbar, wartbar und testbar.
    Gut strukturierten Code (mit klaren Zuständigkeiten, Abhängigkeiten etc) erreicht man häufig am besten mit OO.

    Weniger Code produziert man mit Prozeduralem Code nicht zwingend - oft sogar ganz im Gegenteil.


    Ich habe 5 Klassen, wenn es 5 *verschiedene* Zuständigkeiten gibt. Daran ist dann nichts schlecht erweiterbar.

    Um aber mal deine Argumentation zu benutzen:

    Ich schreibe als 5 Prozeduren, um die 5 Aspekte eines UseCases abzudecken. Das ganze ist dann schlecht erweiterbar.
    Möchte ich in der OO-Variante etwas erweitern, reicht schon eine entsprechende Klasse.
    Ich code somit zielorientiert und nicht overheadorintiert

    Und wo ist jetzt der relevante Unterschied, der das ganze bei OO weniger flexibel macht?

    Ich definiere meine Klassen, die sich *aus* den Use-Cases ergeben. Mit diesen arbeite ich dann.

    Kommen *andere* Use-Cases dazu, muss man in beiden Fällen was anpassen.

    Ich verwende Services in den Fällen, in denen es sinnvoll ist.
    Ich entschiede das, indem ich nicht einfach wild drauf los programmiere, sondern erstmal vernünftig modelliere.

    Einen vernünftigen Entwurf brauchst du *immer*, das löst sich nicht durch die Wahl des Programmier-paradigmas.

    Ähm, wie man"durchhangeln" durch Objektgraphen vermeidet? Information-Hiding!
    Die nutzende Klasse gehen die Interna der genutzen nichts an.
    Aber wir vermeidest du denn prozedural das durchhangeln durch Datenstrukturen, zb um ein bestimmtes Kind eines Baumes zu finden?

    Zu "gutes OO": du redest doch die ganze Zeit von Fowler, lies doch einfach mal irgendwas von ihm? Eine zusätzliche Leseempfehlung habe ich hier auch schon mehrfach geliefert, die können das wesentlich besser erklären als ich...

    *Deiner* Meinung nach. Meiner nicht.

    Gehören *deiner Sicht* nach nicht zusammen.

    Nö, setzt es nicht.
    Spätes Binden = nicht festverdrahtet ist einer der wesentlichen Punkte von OO.

    Konstruierter, als ein Motoradobjekt auf die Innentemperatur der Sonne zugreifen zu lassen, ist nicht möglich gewesen?

    *Wenn* das Motorrad Zugriff auf die Umgehung braucht, dann hat es Zugriff auf die Umgebung, in der es sich befindet.
    *Wenn* die Sonneninnentemperatur ermittelbar ist, gibt es entsprechende Methoden/Objekte/Messages.
    *Wenn* die Anforderungen vorher keinen Zusammenhang von Sonne und Motorrad vorgesehen haben, muss ich anpassen.
    *Wenn* mein Entwurf schlecht ist, muss ich viel anpassen.
    Wenn er halbwegs gut ist, übergebe ich nur dem Motorrad die Umwelt (mit der Sonne) und stelle die Sonneninnentemperatur in der Sonne zur Verfügung, zwei Änderungen + was auch immer das Motorrad mit der Sonneninnentemperatur machen soll.

    *Wenn* für die MotorradMotrensteuerung die SonnenInnenTemperatur relevant ist, gehört das natürlich zur MotorradMotrensteuerung. Warum sollte denn ein künstliches Konstrukt eingeführt werden, welches die Motorensteuerung und die SonnenInnenTemperatur kennt und weiß, wie beide zusammenwirken?


    Aber das ganze mal prozedural betrachtet: Irgendwo gibt es ein Motorrad-Struct und ein Sonnen-Struct.
    Das MotorradMotrensteuerung wird irgendwo in einer lieg geschachtelten Prozedur verändert. Wie bekommst du da jetzt ohne Änderung das Sonnenobjekt hin? Global ein einzelnes Sonnenstruct vorhalten, welches dann jeder verändern kann (zb einfach die SonnenInnenTemperatur negieren)?


    Ich hätte übrigens beim ersten *Wenn* noch mal gewaltig über die Anforderungen nachgedacht.


    Nö, ein klares Indiz, dass du die Anforderungen so umbiegst, dass sie dir besser passen.
    Die Anforderungen war klar: *Der Kontostand darf durch Abheben nicht negativ werden*.

    Wie würdest du es denn machen?
    Wie kommt die entsprechenden Prozedur an die Uhrzeit, die Photovoltaikanlage und den Nachbarn? (ohne, dass du dich durch irgendwelche Structs hangeln musst)

    Durch messages hast du quasi Polymorphie, da verschiedene Objekte verschieden auf die gleiche Message reagieren können.

    *Wenn* es ein grafisches Dreiecks-Element ist, ist es ganz offensichtlich Aufgabe des Dreiecks, sich selbst zu zeichnen. Das ist nämlich dann genau die Funktionalität, für die dieses Dreieck da ist.

    *Wenn* es ein Dreieck ohne Bezug zur Ausgabe ist, ist es nicht Aufgabe des Dreiecks, sich zu zeichnen.
    Ersteres Dreieck kann aber intern alles an letztes Dreieck delegieren, sodass man keine doppelte Funktionalität hat.


    Das schöne an OO-Programmierung: das System ist flexibel und dynamisch. Brauche ich ein Viereck, erstell ich einfach eine Viereck-Klasse, und muss *nichts anderes* anpassen.

    ganze anders bei Prozeduraler-Programmierung: brauche ich Vierecke, muss ich *jede* Prozedur, die mit Formen arbeitet, ändern. An zig Stellen müssen dann die Eigenheiten eines Vierecks bekannt sein, das ganze ist also völlig verstreut im gesamten Programm, anstatt an einer Stelle gebündelt zu sein.


    "Und das macht die OO-Programmierung auch so schön wiederverwendbar.
    Ich schmeisse entsprechende Objekte/Klassen raus, wenn ich sie nicht brauche, oder ich nehme welche dazu und erweitere das System so."
    Da die Abhängigkeiten der Objekte klar definiert sind, hält ich durch rausnehmen auch der Schaden in Grenzen.
    Wenn aber Alles auf alles zugreifen kann, kann ich nichts gefahrlos entfernen, ohne irgendwas kaputt zu machen


    Ich habe ein Struct, was so aussieht:
    Code (Text):

    struct X
    {

    K übergerordnetes struct; //also sprich hier ist eine Referenz drinnnen von dem struct, das X enthält

    T variable1 -> Y variable2 aus T -> variable3 aus Y
    }
     
    und drei Methoden:
    methode1();methode2();methode3();
    Beantworte bitte einmal selbst deine drei Fragen.

    Frage: warum braucht methode1 Dinge von Objekten, die es gar nicht kennt?
    Im Zweifel: es bittet T um die entsprechenden Daten. Das memVariable3 in irgendeinem Unterobjekt existiert, weiß X im Optimalfall nicht

    Keine Ahnung was du damit meinst, global ist da ganz sicher nichts.
    T *ist Teil von* X, Y *ist Teil von* T. nicht Y und T sind global.
    (analog: Stuhl steht im Zimmer, Zimmer ist in nem Haus, was ist daran bitte global?)

    nicht ohne Grund vermeidet man Zyklen, soweit möglich.
    Sind Zyklen in Structs sinnvoll, bei Prozeduraler Programmierung?

    Ja, für sowas kann man Services nutzen. Services sind aber durchaus mehr, als nur reine Prozeduren.

    Die Service-Klasse kann aber in diesem Fall auch keine Service-Klasse, sondern einfach ein "Bank-Objekt" sein, welches die Transaktion vornimmt.

    Weder sind Objektgraph starre Gebilde, noch müssen sie langlebig sein.

    Prozeduren (bzw Aufruf-Graphen) sind starre Gebilde, nicht Objekte!

    OO hat keine feste Struktur, das späte Binden zur Laufzeit ist eines der wichtigsten Merkmale!


    Abgesehen davon: wie entscheiden denn zur Compiler-Zeit statisch gebundene Prozeduren magisch zur Laufzeit irgendwas?


    Die Probleme hast du *genauso* bei PP.
    Das du sie dort nicht und nur bei OO siehst, liegt an deinem Mangelnden Verständnis von OO (und deinem extremen Hype von PP).

    Ansonsten:
    Noch eine ganz allgemeine Frage:
    Wenn du deine Software iterativ aufbaust, am Anfang noch nicht genau weißt wie du was du haben möchtest, wie programmierst du da, bzw. wie gehst du da vor, wie ist dein Buildprozess.
    Das Problem sieht man ja am Konto mit dem negativen Kontostand. Wenn ich ein USeCase habe, den aber erst im laufe meines programmierens erarbeite, wie gehst du da vor?
    Aufwändige Refactorings sollten ausgeschlossen sein, da diese Vorgehensweise (der iterative, top-bottom.... Aufbau) zur Programmiermethodik gehören soll.
    Ich möchte eine Software so aufbauen, dass ich sie immer gut und schmerzfrei erweitern kann, das sehe ich irgendwie bei der PP nicht gegeben, bei OO dagegen schon.
     
  3. Meniskusschaden
    Meniskusschaden Bekanntes Mitglied
    Verstehe ich das richtig, dass du da gerade für die extensive Verwendung von globalen Variablen wirbst? Dann müssen wir kaum über OO diskutieren, denn das wäre auch innerhalb der prozeduralen Programmierung schon weitab vom Mainstream.
    Selbst wenn du diese tolle Strukturierung schaffen würdest, würde dir das wenig nützen, weil dir jedes andere Teammitglied über unkontrollierte Seiteneffekte alles zerschiessen könnte. Diese Art von "Flexibilität" will man durch Einführung des Geheimnisprinzips ja gerade vermeiden.
    Wer hatte denn jemals die Absicht das zu 100% zu tun? Wenn man eine adäquate Klassenhierarchie geschaffen hat, wird innerhalb der Methoden natürlich vieles prozedural gemacht.
    Polymorphie ist nicht dasselbe wie OO, aber ein wichtiger Bestandteil davon. Ein Motor ist auch kein Auto, aber trotzdem ein wichtiger Bestandteil davon.
    Ja. Setzt man in OO-Sprachen mit Objektattributen und -methoden um und in prozeduralen Sprachen mit Verbundtypen und Funktionszeigern. Das sind zwei unterschiedliche technische Möglichkeiten, OO-Design zu implementieren.
    Natürlich benötigt man keine Zeichenmethode, wenn man nicht zeichnen will. Wenn man aber ein Shape-Objekt haben möchte, das ein Dreieck zeichnet, kann es sich die Mathematik doch von einem Triangle-Objekt erledigen lassen. Dazu muß man auch keine Prozeduren raus werfen oder rein kopieren, sondert verwendet einfach seine Triangle-Klasse wieder.
     
    mrBrown gefällt das.
  4. AndiE
    AndiE Mitglied
    So richtig kann ich der Argumentation nicht folgen. Ich habe doch als erstes eine Use-Story. Nehmen wir mal als Beispiel eine Lieferscheinerstellung. In einem Lager gibt es Waren, die an Kunden verschickt werden und dabei werden Lieferscheine erstellt. Ich würde ich als erstes den "best case" modellieren, aber schnell darauf kommen, dass ich erst dem "worst case" benötige. Im "worst case" kennt das System weder einen Kunden noch eine Ware, und natürlich keinen Lieferschein. Im "best case" sind Kunde und Ware im System enthalten. In Falle des "worst case"muss ich also erst die Ware erstellen und dann den Kunden, und dann beides in einem Lieferschein verbinden. Wahrscheinlich würde ich die Waren und die Kunden noch als Collections erstellen.
    Diese "use cases" "Ware erstellen" und "Kunde erstellen", erstellen ein neues Objekt, dass sie einfach den vorhandenen Collections hinzufügen.
    Im Normalfall würde ich in der OOP die attribute private deklarieren und die getter und setter als public. Offensichtich wurde die Methode Ware.entimm(int anzahl) auch public sein. Da ich eine Formularansicht habe, habe ich auch immer ein aktuelles Objekt der Klasse Ware, das ich bearbeite. Und der Lieferschein wächst bei Ausführung dynamisch.
    Will ich nun eine Anzahl Fuhrunternehmer mit verwalten, die die Lieferungen zum Kunden bringen, dann habe ich zwischen der Klasse Fuhrunternehmen und der Klasse Lieferscheine ein m:n-Kardinalität. In der Regel würde ich sogar eine Super-Klasse Geschäftspartner erstellen, von der Kunde und Fuhrunternehmer "erben", weil diese Klasse die anderen beiden generalisiert.

    Natürlich könnte ich das auch prozedural machen, aber "addnewItem()" liest sich schwerer und ist unverständlicher als "ItemList.addItem()". Bei zweitem weiß ich, wer was macht, und durch die längere Schreibweise haben wir beim ersten auch mehr Fehlerwahrscheinlichkeit.
     
  5. mrBrown
    mrBrown Super-Moderator Mitarbeiter
  6. knotenpunkt
    knotenpunkt Neues Mitglied
    @mrBrown Nein, hatte nur in letzter Zeit kaum Zeit^^, aber ich bereite schon den nächsten Diskussionschritt/Beitrag in diesem Thread hier vor^^
     
  7. knotenpunkt
    knotenpunkt Neues Mitglied
    Hey,

    Da wäre ich aber an einem Beispiel interessiert^^

    Wie genau definierst du erweitern?
    erweitern heisst für mich, teilweise bestehende Funktionlität weiter verwenden und diese um einen Gesichtspunkt zu erweitern. Also eine weitere Prozedur, die bestehende Prozeduren in irgendeiner Weise verwendet.


    Es kommt glaube ich darauf an, wie man erweitert.
    Es gibt meiner Meinung nach zwei Erweiterungsklassen (ich verwende hier den Begriff Klasse nicht im Zusammenhang von Programmierung, sondern einfach als normalen Sprachgebrauch^^)

    Erweitere ich horizontal oder vertikal?
    Sprich erweitere ich Funktionalität aussen rum (heisst ich verwende subsysteme einfach so wie sie sind -> So ein Art Schichtensystem) oder erweitere ich switch-cases, in der oo dann weitere Typen, polymorphe Aufrufe (switch-case und polymorphe Typen, sind ja das gleiche^^)

    Bei zweiterem hast du recht, da muss ich bei beiden was anpassen.
    Bei ersterem, die Frage an dich, wie erweiterst du sinnvoll in der oo "Funktionalität aussen rum".
    //Ich habe dazu noch keine Meinung, würde mich auf ein Beispiel von dir freuen^^


    Zu dem Switch-Case habe ich aber gleich mal ne Frage:
    {
    data x;
    data y;
    data z;

    switch(variable k)
    case 1;
    case 2;
    usw
    }

    im case 1 werden x und y verändert im case 2 werden y und z verändert/zugegriffen/oder whatever.
    variable k steht erst zur Laufzeit und erst bei aufruf dieses Blocks { } fest!
    wie würdest du das sinnvoll ins oo umformen?

    ich würde sagen es ist schwierig solange k keinen fest defnierten/unflexiblen Zustand aufspannt.
    Außerdem ist es kritisch für oo, da sowohl case 1 als auch case 2 y benötigen, entsprechende cases aber nicht jeweils x und z benötigen.

    So konstruiert finde ich das Beispiel nicht. Es zeigt, dass ich in speziellen Fällen, eben solche Anforderungen brauche und dies in der pp kein Problem darstellt.

    Punkt 1 (Motorrad und Umgebung):
    Das Motorrad hat die Umgebung und die Umgebung hat das Motorrad -> zirkuläre Abhängigkeit (du hast ja selbst geschrieben dass zirkuläre Abhängigkeiten schlecht sind)

    Punkt 2 (Sonneninnentemperatur und entsprechende Methoden/Objekte/Messages)
    Ich hatte ja ähnliches beispiel:
    struct X
    {
    T mV1 -> mV2 -> mv3 ->.....
    }

    Du meintest hier, das im best case X mV2 und mV3...... nicht kennen sollte

    Auf die Sonneninnentemperatur bezogen, würde das folgendes bedeuten:
    Das Motorrad kennt nur die Umgebung, also nicht die Sonne
    Oder noch überspitzter, Der Motorradmotor kennt nur sein umliegendes Motorrad und nichtmal die Umgebung.

    Wie kommt also das Motorrad zur Sonneninnentemperatur.
    Wenn du die Umgebung jetzt zu einer Art Facade umfunktionierst, dann hast du da zig-Tausend Methoden drinnen stehen. Motorrad fragt Umgebung ->giveMeSonneninnenTemperatur
    und die Sonne delegiert das weiter z.B an die WeltallFacade
    Das würde bedeuten, ich müsste in der Umgebung alle Methoden mit aufnehmen, die die memberVariable der Umgebung auch besitzen (und von aussen, hier jetzt das Motorrad benötigt werden) usw.
    Das ist doch ein Krampf!

    Also wie würdest du das richtig designen?


    Punkt 3 (schlechter Entwurf):
    Was ist ein guter und was ist ein schlechter Entwurf. Vllt hättest du da ein paar Antworten für mich auch auf konkreter Ebene und nicht nur abstrakt^^

    Punkt 4 (Sonnen-Struct)
    Warum nicht global, zumindest Teilglobal.
    Sollte es in einem anderen Modul (Modul !=oo) liegen, kann man sich ja überlegen, das ganze nur über eine Wächterprozedur accessable zu machen.
    Was mir hier aber wichtig ist, ich füge hier nicht daten und behaviour zusammen, sondern ich habe nur Behaviour.
    Die Daten können meintwegen in der Datenbank liegen, sie können via fremd-API zugreifbar sein.
    Und vor allem, die Daten haben keine Abhängigkeiten untereinander.
    Um zu deinem Bankkonto zurückzukommen: Irgendwie hat dein Bankkonto sagen wir mal zwei mem_variablen, die was auch immer aussagen, was genau ist egal^^

    möchte ich gewährleisten, dass mem_variable1 und 2 aus dem Bankkonto nur eine bestimmte Zustandsmenge annehmen dürfen, dann baue ich dafür eine Wächtermethode, die eben nur Elemente aus meiner eingeschränkten Zustandsmenge zulässt, oder diese sogar selbst baut! (angereichterte Wächtersetterfunktionen oder angereichterte Buisnessfunktionen). Das coole an der prozeduralen Programmierung ist aber jetzt im Vergleich zur OOP, dass diese Daten hier nicht in einer location gekapselt werden müssen. Zudem kann es sein, dass diese zwei mem_variablen auch nur in der einen Prozedur zueinander in Relation stehen.

    Ich mache die Anforderungen komplexer.
    Mich würde interessieren, wie du das mit meiner Anforderung machen würdest.
    Wie ich es mit deiner machen würde, steht direkt über diesem Zitat..... Eine Wächterprozedur, die deinen Use-Case abdeckt.

    Falls entsprechende Daten im Speicher stehen sollten, habe ich mir vorher optmierte direktZugriffCachingstrukturen gebaut. Falls nicht:
    In der OOP bin ich mehr oder weniger gezwungen einen Graphen aufzubauen und damit zu arbeiten.
    in der PP kann ich optimierte Datenstrukturen her nehmen. Dazu zählt auch das Wissen über Alignment im Speicher und anderes (diverse Hashtabeln, Binärvektoren, etc pp). Wenn ich möchte kann ich natürlich auch eine Baum/Graphstruktur aufbauen und damit arbeiten. Ich bin in der PP diesbezüglich sehr flexibel.
    Sollten die Daten in der Datenbank liegen, kann mich mir die Effizienz des Datenbanksystems zu nutze machen^^

    Sowie ich dich und auch die OOP im Allgemeinen verstanden habe, ist das aber streng genommen keine OOP wenn ich mir die Daten zusammenglaube und dann an einer Stelle einen Algorithmus ausführe. OOP besteht ja aus verteilten Algorithmen.
    Meiner Meinung nach kann man mit verteilten Algorithmen einen "gesammelten" Algorithmus (bspw. eine Prozedur)
    in seiner Gesamtheit nur abbilden, wenn ich State und Steuerungsvariablen mitdurchschleife etc pp. Und wenn dann noch transaktionen/atomarität etc pp dazukommen....... gute nacht^^

    Der Testbarkeit zuliebe finde ich bei verteilte Algorithmen jetzt auch nicht wirklich so gut gegeben.


    Wie genau sieht für dich Delegieren aus?
    DreiecktGraphisch-> ruft methode getXCoordinate() auf in DreieckNormal und dieses -> ruft getXCoordinate() auf sich selbst auf. Das ist für mich eben overheadorientiertes programmieren^^


    Wie soll es dann an die Daten kommen?
    Ok ich kann facadenhaft die Schnittstellen platt klopfen, sprich ich biete in T Methoden an, die nichts anderes tun, als einfaches Weiterdeligieren an Methoden von memVariable3
    Das ist doch nicht sinnvoll.


    Naja ich finde schon, ob ich jetzt via eines direkten Access an die Daten komme oder via Hangelns, ist für das Ergebnis egal. Ich habe in der Konsquenz dann die Daten.
    Nur Zweiteres bedeutet viel Overhead in der Umsetzung

    Zyklen in prozeduraler Programmierung? Wie meinst du das?
    Wie du vorhin ja selbst geschrieben hast: Die Motorsteuerung muss Zugriff auf sein Embeddendes Objekt, also die Umgebung haben, somit würde ich zirkulär programmieren.
    Also wie würdest du die Motorsteuerung auf die Innentemperatur der Sonne zugreifen lassen?


    Und dann brauche ich später Transfers zwischen Banken usw.
    Jetzt könnte ich ja gemäß Bottom-Up hier wieder ein "SammelObjekt"-Schreiben. vllt. eine Bankenvereinigungsklasse^^
    Möchte ich aber jetzt nur einen Transfer innerhalb einer Bank machen, dann muss ich die Schnittstellen aus der Bankklasse wieder facadenhaft nach aussen reichen, also in der Bankenvereinigungsklasse Delegationsmethoden anbieten. Oder die Bankenvereinigungsklasse muss wieder Internas rausgeben, sprich eine Methode getBank() haben. Diese Lösung findest du selbst nicht so gut, wie du bei meinem Beispiel mit X->mem1->mem2->mem3 geschrieben hast.

    Die Struktur der Prozeduren ist starr, ja das ist richtig^^
    Die Ausführung dagegen ist hochdynamisch und zwar wesentlich dynamischer als bei Objekten^^
    Und das möchte ich jetzt etwas näher erklären:
    Prozeduren:
    Jeder verschiedenartige Input hat ein anderes Verhalten zur Folge (auch durch switch-cases gegeben)
    das macht das meinetwegen auch etwas schwerer testbar, aber somit auch hoch dynamisch^^

    Objekte:
    Jedes andere Objekt mit anderem State verhält sich eigentlich genauso wie ich es eben unter dem Punkt Prozedur geschrieben habe.
    Möchte ich in einem Batch-Prozess dieses Verhalten hervorrufen, muss ich sowas in der Art machen:
    new ActionObjekt(data1,data2,data3)->process(); //anschließend kann der GC das ActionObjekt wegwerfen.
    Empfinde ich nicht so sinnvoll, hier den Heap und den GC zu stressen, obwohl man auch direkt callFunction(data1,data2,data3) aufrufen könnte.

    Also habe ich in der Objektwelt eher starre Gebilde
    Irgendwo mal vereinzelt ein C=new Objekt(dataA,dataB,dataC); und dann rufe ich auf C methoden auf.
    Dieses verhalten ist gemäß der konstruktordaten dataA bis dataC, sehr eingeschränkt.
    Flexibilität sehe ich hier nicht!


    prozeduren, durch switch-cases und zwar je nach aktuell stattfindender parameterübergabe.

    OO kann das gleiche Verhalten vorweisen, aber wie ich schon direkt über diesem zitat geschrieben habe:
    ActionObjekte sind nicht so sinnvoll.
    So zeigen zwar auch Objekte in dem Punkt dynamisches Verhalten auf. Diese Dynamik ist aber nicht flexibel, da die Objekte mit der Dynamik zwar konfiguriert werden (Konstruktoraufruf new Object(dataA,dataB,dataC)), diese Flexibilität dann aber nicht weiter zur Laufzeit haben. (sonst wären wir ja wieder bei den ActionObjects^^)
    Ich denke du kannst nachvollziehen, was ich meine, oder?

    Touche^^, aber meinen Text empfinde ich als richtiger^^



    Und wo ist dann der Unterschied zu gut strukturiertem prozeduralen Programmieren?

    Ich wollte damit nur klarstellen, dass OO nicht Polymorphie ist.
    Polymorphie kann in OO verwendet werden, genauso aber auch in prozeduraler Programmierung



    Zweiteres setzt vorraus, dass ich die ItemList vor mir habe.
    Bei ersterem muss ich die ItemListe nicht vor mir haben.
    Wenn ich als Identifikator bspw. eine ID, ein SuchString etc pp übergebe, dann liegt es in der Verantwortung der addNewItem() Prozedur die Daten zu holen, und nicht mir bei mir als Client, diese Daten bereitzustellen bzw. vorzuenthalten. Und das ist schon sehr wertvoll^^


    Hier sehe ich allgemein das Problem, dass man viel overhead programmiert. Und zwar: Einen Wrapper um bestehende Collection-Klassen.


    So jetzt hätte ich mal noch ein paar Praxisbeispiele, wo ich versucht habe einen OO-Ansatz umzusetzen, aber nicht wirklich glücklich darüber bin. Im folgenden werde ich ausführen, warum ich darüber nicht glücklich bin, bzw. welche Probleme ich da so sehe^^


    Problem 1:

    Wenn ich jetzt bei den Collection-Klassen bleibe:
    Ich habe oft folgendes Problem:
    wenn ich Daten abspeichere, dann speichere ich die unterschiedlich indexiert ab.
    Sprich einmal sequenziell in einer ArrayList
    Ein zweitesmal in einer HashMap mit dem Key Name
    Ein drittesmal in einer Hasmap mit dem Key Alter


    class User
    {
    string name;
    string alter;
    string mail;
    string gender;
    //weitere....
    }

    class IrgendEinUserContainer
    {
    ArrayList<User> sequenziell;
    HashMap<String,User>indexedByName;
    HashMap<String,User>indexedByAlter;
    }


    So kann ich später je nachdem mit welchem Suchindex ich drauf zugreifen möchte, entsprechend schnell an das UserObjekt gelangen.
    Ist das sinnvoll das so zu machen?
    Weil bei jedem push/remove muss ich alle indexStrukturen anpassen.

    Wie ich SQL an der Stelle liebe. Einfach ein entsprechender Suchstring eingeben und ich bekomme super schnell mein Ergebnis (das habe ich geschrieben, weil ich früher viel web-zeugs gemacht habe^^)

    Möchte ich in der OO-Welt einen weiteren Index hinzufügen, muss ich einiges anpassen. Bei SQL dagegen interessiert mich das nicht!



    Problem 2:
    verteilte Algorithmen:

    @mrBrown du hast vorhin ja geschrieben, wenn X überhaupt Informationen von memVariable3 braucht.
    Wenn nicht, dann impliziert das, dass in memVarbiable3 selbst entsprechender Algorithmus definiert ist.
    Aber das geht ja leider nicht überall, dass man Algorithmen aufteilt.
    Und genau deshalb brauche ich managed/service-Prozeduren, wo der Algorithmus an einer zentralen Stelle definiert ist und sich die Daten holt/via Argumente bekommt und basierend auf diesen dann entsprechendes Programmverhalten zeigt^^

    Ich habe bspw ne RestApi von einer Gartenbeleuchtung

    Was für Module habe ich:
    Kommunikationsmodul (tcp/connection.... Restparsing etc pp)
    SmartHomeModul
    BelechtungsschalterHighLevel
    RelaisSchalterLowLevel


    Wo bearbeite ich den Request?
    Der Request kommt im Kommunikationsmodul an..... theorethissch könnte ich ihn hier schon komplett abarbeiten
    dazu würde ich mir entsprechende State-Daten aus untergeordneten Modulen/Objekte holen

    Ich kann den Request aber auch irgendwie weitergeben

    Aber dann hat mein Kommunikationsmodul nur noch zur Aufgabe Requests weiterzudeligieren und im Falle eines "Protokollfehlers" rauszufiltern und wegzuschmeissen.


    Ok funktioniert alles -> ich delegiere weiter an das SmartHomeModul
    Warum würde ich als prozedural-denkender Mensch spätestens hier die die komplette Logik verbauen?

    Das mag ich erklären:

    Das RelaisSchalterModul hat dafür Sorge zu tragen, dass das Relai-Modul immer nur alle 2sec einen Umschaltvorgang tätigen darf, sollte es sich bei einem Mehrkanalsrelais um den gleichen Relaikanal handeln.

    Das BeleuchtungsschalterHighLevel Modul sorgt dafür, dass ein entsprechender User immer nur alle 30sec etwas schalten darf.

    Ausserdem sollte in der Requestantwort detailiert stehen, im Falle eines Negativs, warum der Schaltvorgang nicht funktioniert hat.

    Zudem, sollte das Relaismodul in der letzten Stunde bereits 30 Schaltvorgänge getätigt haben und ein User X selbst in der letzten Stunde 20 Schaltvorgänge gemacht haben, dann wird das ganze System für 3h gesperrt

    Was ich damit sagen möchte, das ist alles so sehr verwoben, dass es nicht viel Sinn macht zu versuchen den Algorithmus da aufzuteilen.


    Vllt möchte ich auch noch folgendes miteinbauen:
    Wenn User X 50RestCalls gesendet hat und nur 10 Schaltvorgänge getätigt hat, das Relaismodul 5 Schaltvorgänge verzeichnet hat und das alles in den letzten 40min, auch dann sollte eine Sperre, diesmal für 5h eingerichtet werden.

    Sprich ich brauch in dem Use-Case bzw. in dem Algorithmus, Daten aus allen Modulen/Ebenen (vorwärt/rückwärts/zirkulierend, etc pp^^)

    Eine Prozedur, die die Daten erhält, sie erhalten kann oder whatever ist hier wesentlich besser geeignet als der Versuch den Algorithmus aufzuteilen, was ja auch gar nicht geht.


    Problem 3

    Vllt. geht das ganze ja doch?!

    Also was ich auch schon öfters gemacht habe:
    Bei vielen MethodenCalls schleife ich einen Art Kontext mit durch^^

    Bei einer Mischpultsteuerung die ich mal programmiert habe, habe ich bspw. folgenden Kontext mitdurchgeschleift.
    Mitdurchgeschleift bedeutet in dem Fall, dass in einer Event-basierten-Architektur, auch die die abgefeuerten Events, Delegationen, etc, pp den Kontext miterhalten haben.

    Z.b.

    channel5.setEQDBHighLevelBand(3,kontext);

    kontext enhält folgende Daten
    {
    isNetwork
    isUserCall
    isAutomaticCall
    isDCACall
    }

    Warum brauche ich die Daten?
    Im Falle des DCA-Calls unterbinde ich somit Schleifen, sollte es sich um einen Fader handeln, der selbst in einer DCA-Gruppe ist.
    IsNetwork bedeutet, ich habe die Parameteränderung von einem ClientRechner aus dem Netwerk erhalten/oder vica versa...... Das isNetwork unterbindet dann später im NetworkEventProcessor, dass der Request wieder zurückgeleitet wird, was dann übers netzwerk eine endlosRekursion zur Folge hätte

    ....

    Also wie man sieht, kann man Algorithmen schon etwas aufteilen, ABER ich muss Steuerungskontext Daten mitdurchschleifen?!
    Eine andere Lösung sehe ich da nicht?!
    Oder seht ihr da eine andere Lösung?


    Also nochmal zusammenfassend was stört mich an OO:
    -der Versuch einen zentralen Algorithmus in einen dezentralen umzuwandeln, was oft nicht funktioniert
    -ich brauche das Objekt an der Stelle, wo ich etwas mit Ihm bzw. dessen Daten anstellen möchte //siehe addItem Beispiel
    -unnötige Abhängigkeiten -> sobald ich membervariablen in einem Objekt zusammenfasse und auf diesem Methoden definiere, so ist die Methode immer abhängig von ALLEN Membervariablen, auch wenn nicht alle benötigt werden.
    -sehr unflexibel -> siehe Beispiel mit ActionObjekt/NormalenObjekt/SwitchCases
    -weiteres


    soweit mal wieder


    lg knotenpunkt
     
  8. AndiE
    AndiE Mitglied
    Zu 1.:
    Hier kommt es doch auch auf die Modellierung an. Angenommen, ich hätte eine Bibliotheksverwaltung, und das Buch-Objekt hat die Membervariablen (Platz,Titel, Autor, Kategorie, Umfang). Dann kann ich ja nach den ersten 3 Variablen indizieren. Dann habe ich doch zumindest 2 Listen, die aufsteigend Titel und Autor beinhalten.
    Die OOP entspricht für mich dem natürlichen Denken.

    Erzeuge ein Buchobjekt. Nimm das Bibliotheksobjekt. Füge de Bibliotheksobjekt das Buchobjekt hinzu.

    Und damit fertig. Was da im Hintergrund passiert, ist mir egal. Das macht doch das Programmieren viel übersichtlicher und damit auch wartbarer.

    Ich brauche eine inizierung? Wer besitzt die? Sicher das Bibliotheksobjekt. Das hole ich mir und kann dann damit arbeiten.
     
  9. mrBrown
    mrBrown Super-Moderator Mitarbeiter
    Gefühlt alles, wo Polymorphie benutzt wird...


    Ich definiere das kaum anders - dem Programm neue Funktionalität hinzufügen.
    In welcher Form ist egal, relevant ist nur, dass das Programm am Ende mehr Funktionalität hat.

    Okay, um meine ursprüngliche Aussage anzupassen: man muss die für den neuen Use-Case relevanten Teile anpassen ;)

    Das "Funktionalität aussen rum" ohne Probleme klappt, ist doch einer der wesentlichen Punkte von OO. Bestehenden Teilen ist es vollkommen egal, wie sie genutzt werden - solange die der Spezifikation entsprechend genutzt werden, und das erzwingt man z.T. durch Kapselung.

    Ja, genau wie ein if+goto das gleiche ist wie while und for...also theoretisch schon, praktisch wird es aber ganz sicher nicht so genutzt^^


    Irgendeinen Prozederuralen Code dahin klatschen und den zu OO umformen geht immer schief.
    Liefer passenden Kontext dazu, dann liefer ich dir den passenden Code ;)

    Wenn das interne Daten eines Objekts sind, ist da durchaus auch mal ein Switch valide, u.U. fährt man aber schon da mit Command-Objekten besser.


    Ein Motorrad, welches die Innentemperatur der Sonne kennt, zeigt vor allem, dass man, um sein Argument zu unterstützen, die absurdesten Dinge konstruiert ;)

    Deshalb vermeidet man sie soweit möglich - ein Beispiel, in dem man sie zwingend braucht, kann man natürlich immer Konstruieren ;)

    Wenn(!) so etwas völlig absurdes nötig ist, könnte man über die Umgebung an die Sonne kommen und über das Motorrad an dessen Umgebung. Aber dann würde es bei mir daran scheitern, das man die Innentemperatur der Sonne nicht einfach weiß ;)

    Aber mal andersrum gefragt: Wie würdest du so etwas sauber(!) designen?

    Schlechte (also richtig beschissen und absolut unbrauchbarer) Entwurf: Motoradmotor braucht Sonneninnentemperatur
    Besserer Entwurf: Motoradmotor braucht Umgebungstemperatur, welche die Umgebung hat. Wie die berechnet wird, und ob es Überhaut eine Sonne gibt (Meine Motorräder fahren nämlich nur in anderen Galaxien, in denen es entweder keine oder sieben Sonnen gibt), interessiert den Motor nicht sondern weiß die Umgebung.

    Ich bin ein ziemlich naiver programmiere, sehe also diese beiden mem_variablen und änder sie einfach, wie ich das grad brauche. Von der Wächtermethode weiß ich nichts, die ist ja irgendwo völlig anders.
    Mein etwas weniger naiver Kollege will die Variablen nicht direkt nutzen, sondern denkt sich "ah, um die zu schützen braucht ich eine Wächtermethode" und schreibt sich einfach eine passende. Natürlich eine völlig andere, weil er deine Wächtermethode nicht kennt.
    Die mem_variablen sind aber Unternehmensrelevant und dürfen nicht getrennt geändert werden. Dummerweise weiß ich das nicht und änder sie beliebig und mein Kollege ändert sie leider falsch.

    Klingt sinnvoll, oder?

    Du gehst abet nicht wirklich zu nem Kunde und sagst dem "ich habe deine Anforderungen komplexer gemacht, weil deine Geschäftsregeln waren irgendwie doof und ich wollte sie gern anders. Das dein Geschäft jetzt nicht mehr Funktioniert ist mir egal. Benutz aber bitte meine Wächterprozedur".

    Was ich mit deiner kontextlosen Wächterprozedur mache, hab ich ja auch schon gesagt: sie ignorieren. Warum sollte ich auch eine wild im Programm stehenden Prozedur nutzen, wenn die Daten eh global verfügbar sind und ich sie viel einfacher direkt benutzen kann?


    und das jetzt bitte in der prozeduralen Sprache Pascal.

    Ich habe keine Ahnung, was du plötzlich zu verteilten Algorithmen meinst und wie du dazu kommst und was diese mit dem Thema zu tun haben, deshalb ignorier ich's einfach mal...


    Dann nimm ne Sprache, in der sowas inlined wird, und es nur noch ein direkter Speicherzugriff ist. (Um's dir leichter zu machen: bist sogar schon auf der passenden Seite dafür ;) )
    Und ja, genau so sieht Delegation aus.

    Wenn(!) die internen Daten relevant für außen sind, doch, dann ist es sinnvoll, für diese eine Schnittstelle zu bieten.
    Um mal ein reales Beispiel zu geben Üblicherweise hab ich auch keinen direkten Zugriff auf dein Portmonee und kann mir einfach Geld rausnehmen, sondern frage dich, wenn du mir Geld geben sollst.

    So meine ich Zyklen in Structs:
    Code (Text):

    struct A {
      B b;
    }
    struct B {
      A a;
    }
     
    Wenn nötig kann man durchaus zirkuläre Referenzen nutzen - im Idealfall aber nur als Laufzeit und nicht Kompilezeit-Abhängigkeit. In den meisten Fällen kann man es aber vermeiden.

    Und wie kommst du prozedural an die Bank?
    Der Nutzer hat nur den Banknamen eingetippt, wird das in deinem Programm magisch zur Bank?
    In OO wäre das einfach ein bankRepo.getBankByName(name) - ein Interface. welches für genau den Zweck da ist.
    Aber spaßeshalber einfach mal von der echten Welt ableiten: Huch, sieht ja da wie in der OO Variante aus. Bestimmt total unhilfreich für jeden Domänenexperten, wenn das Programm die Domäne eins-zu-eins abbildet....


    Also hast du Code, den fast jeder direkt als absolut unbenutzbar abtun würde?

    In meiner lustigen, grad erfundenen Prozeduralen Sprache erzeugt jeder Prozeduraufruf 17GB an Daten auf dem Heap, welche mit 1Kb/sek erzeugt werden.
    In realen OO-Sprachen kann das alles nur mit Allokation auf dem Stack oder sogar ohne jegliche Speicher-Allokation für Objekte passieren.

    Also haben wir: Prozedural super langsam vs OO super schnell
    (Vielleicht fällt dir jetzt auf, dass du aus "Konzept vs Konzept" ständig "schnelle Laufzeitumgebung vs langsame Laufzeitumgebung" machst - wenn du lieber darüber reden willst, solltest du einen dazu passenden Thread aufmachen.)
    Für dich ist also "3 Variablen im Speicher ablegen und eine Funktion aufrufen" deutlich unflexibler als "3 Variablen im Speicher ablegen und eine Funktion aufrufen"? Kann man so sehen, ist aber etwas merkwürdig...

    Nö, kann ich nicht, weil es Unsinn ist. Siehe den Punkt hier drüber.


    Was bedeutet für dich "gut strukturiert"?
    Zusammengehörende Dinge zusammen legen ist es nicht, einzelne Teile nur bestimmte Dinge machen auch nicht (weil zu unflexibel).
    Wenn ich alle deine Erklärungen zusammen nehme, hab ich völlig unstrukturierten prozeduralen Code vor Augen.

    Das ist der Unterschied zwischen Dependeny Injection und dem großen Haufen Code, ersteres hat sich nicht ohne Grund durchgesetzt ;)

    Glückwunsch, du hast erkannt, dass deklarative Sprachen oft toller sind als Prozedurale Sprachen :) Gehe ich vollkommen konform mit ;)

    Da ich später noch ein Bluetooth-Kommunikationsmodul, über das die gleiche Funktionalität verfügbar ist, hinzufügen möchte, beschränken sich die Kommunikationsmodule nur auf den Kommunikationsteil, ist doch völlig logisch?



    Oder: der Algorithmus, wie du ihn dort aufgeschrieben hast, ist zu kompliziert.
    Einfach mal vernünftig modellieren, dann kann man damit vielleicht was anfangen ;)

    Wie würde denn die Prozedur aussehen? 2345 Zeilen und die tiefste Schachtelung etwa 19? ;)

    Ich hab keine Ahnung von der Domäne, deshalb kann ich nur "vielleicht, vielleicht auch nicht" sagen...
    Wie schon mal gesagt: völlig Ahnungslos vollkommen kontextlose Dinge zu modellieren geht immer schief, und in dem Fall hab ich weder Ahnung noch Kontext.
    Generell gibts aber durchaus sowas wie Context and Dependency Injection, ist oftmals ganz hilfreich bei sowas...

    Ganz im Gegenteil: meistens funktioniert das wunderbar. Macht man btw auch in prozeduraler Programmierung (ich weiß, ist schwer zu glauben, aber die meisten Programme haben mehr als eine Prozedur).

    Joa, üblicherweise brauche ich Objekte, wenn ich sie ändern will. Finde ich jetzt auch nicht sehr störend.
    Wenn ich irgendein Objekt gar nicht haben will, will ich es auch üblicherweise nicht verändern (ich find es schon ganz geil, dass meine Kaffeemaschine nicht plötzlich die Sonneninnentemperatur ändert...)

    Stichwort: sinnvolle Modellierung. Wenn man nicht zusammengehörende Daten beliebig zusammenfasst, bekommt man *immer* Probleme - völlig unabhängig welches Konzept und welche Sprache und sogar in der realen Welt.

    "sobald ich [ganz prozedural viele Variablen in einem Struct] zusammenfasse und [Prozeduren, die dieses Nutzen] definiere, so ist die [Prozeduren] immer abhängig von ALLEN [Variablen des Structs], auch wenn nicht alle benötigt werden."

    Wie gesagt: der Punkt an sich ist schon völliger Unsinn.
     
    Zuletzt bearbeitet: 20. Juni 2018 um 23:45 Uhr
  10. AndiE
    AndiE Mitglied
    Einsprungspunkt in PP:
    Code (Text):

    // Header
    void functuin1();
    void function2();

    main(){
    int auswahl;
    switch(auswahl=menu()){
    case 1: function1();
    break;
    case 2:function2();
    break;
    }
    Einsprungspunkt in OOP
    Code (Java):
    public class App{

    static vord main(){
    DatenObjekt do= new DatenObjekt();
    AnsichtObjekt ao= new AnsichtObjekt(do);
    ao.start();
    Auf den ersten Blick scheint hier nur die switch-Anweisung in die AO-Klasse verrutscht. Für mich ist es aber unübersichtlich, die Header-Dateien vorher anzugeben. Noch blöder fand ich bei C, dass auch die structs in der Header-Datei liegen. Bei C++ hat man wenigstens die gesamte Klassendeklaration in der Header-Datei und nur die Methoden in der cpp.

    Und hier unterscheidet sich OOP von PP ganz gewaltig. Auch sowohl die Konsolenanwendung als auch eine GUI wird im PP immer eine Zahl für die Auswahl zurückgeben. In der OOP ist das nicht so eindeutig. Der ActionListener wird bei einer GUI die entsprechende Methode aufrufen. Auf der Konsolenebene kann man das mit einem switch machen, aber auch anders.

    Und genau das ist der Vorteil. Ich muss nicht den ganzen Top-Down-Botton-Up-Zweig durchgehen und Dumpings setzen. Ich kann auch seperat Funktionalitäten( Klassen) schreiben und separat testen. Das könnte ich bei PP auch, aber ich kann Klassen durch einen Status beschreiben und den testen. Bei der PP werden Datenobjekte nie als solches gesehen und ich kann ihren Status auch nicht testen. Das ist von der Modellierung her ein gewaltiger Unterschied.

    Und aus diesem Ansatz heraus, das Objekte einen Status haben, erwächst ja auch die Grundlage, dass Nachrichten bei der OOP den Status der Objekte ändern. Dadurch kann ich letztendlich auch Multitasking und Threads(parallele Abarbeitung) machen, aber das nur so am Rande.
     

Die Seite wird geladen...

OO ist gescheitert - Ähnliche Themen

Weihnachtsbaum implementieren gescheitert.
Weihnachtsbaum implementieren gescheitert. im Forum Java Basics - Anfänger-Themen
Tannenbaum implementieren gescheitert
Tannenbaum implementieren gescheitert im Forum Java Basics - Anfänger-Themen
Code auf Klasse verlegen - Mission gescheitert
Code auf Klasse verlegen - Mission gescheitert im Forum Java Basics - Anfänger-Themen
Gescheiterte Projekte bzw. Konsequenzen
Gescheiterte Projekte bzw. Konsequenzen im Forum Plauderecke
Thema: OO ist gescheitert