Ankündigung

Einklappen
Keine Ankündigung bisher.

Clean Code

Einklappen

Neue Werbung 2019

Einklappen
Das ist ein wichtiges Thema.
X
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • #31

    Zitat von BlackScorp Beitrag anzeigen
    Die Tasks kriegen dann als Text sowas wie, Create new UseCase x, Append Repository Y to UseCase x, Modify UseCase z, da ich immer wieder den gleichen Prozess habe, ganz egal welches Feature auf dem Plan steht, bin ich in der Lage relativ prezise die Zeiten anzugeben so dass das QA Team den Testzeitpunkt schedulen kann.
    Aha. Egal wie komplex ein Problem auch ist... Wenn ich es in einen UseCase packe ist es immer zeitlich berechenbar.
    Wenn ich ein komplexes Problem habe, dann muss ich dieses Problem in Teilprobleme zerlegen. Und zwar solange, bis ein Teil-Problem nicht mehr komplex genug ist, um es weiter zu zerlegen. Das Prozedere lässt sich gut mit einer Mindmap darstellen und die benötigte Zeit entsprechend berechnen. Deine Teilschritte auf oberster Ebene sind jeweils immer UseCases. Darunter eine Mischung aus irgendwie verdrahteten Services (soweit ich das jetzt verstanden habe).
    Tendentiell sind die "UseCases" bei nicht-trivialen Problemen von der eigentlichen Problemlösung mindestens 2 Ebenen entfernt - jedenfalls bei mir. Bei mir sind es Sub-Controller bzw. Composite Services.

    Hier mal ein relativ oberflächlicher Prozess um Bestellungen einem Marktplatz verschiedener Clients zu beziehen:



    Wie teilst du diesen Prozess in deinem Modell auf?

    Zitat von BlackScorp Beitrag anzeigen
    Nun beginne ich dann entweder mit vorhandenen Tests oder ich erstelle neue Tests, die Test klassen sind relativ klein, dadurch lese ich weniger den Sourcecode und Programmiere weniger, ich muss mir nicht jedesmal überlegen, wie war das noch mal für diesen Testfall, wenn ich einen neuen Testfall brauche, erstelle ich eine neue Fakerepository mit den Entities die ich für den Testfall brauche.
    Dieser Idealvorstellung hing ich noch mal nach. Irgendwann empfand ich es als lästig, Tests für alles zu schreiben. Triviale Dinge zu testen, ist ziemlich wertlos. Aber egal, das verfremdet das Thema zu sehr

    Zitat von BlackScorp Beitrag anzeigen
    Nun gehen wir mal von euren Beispielen aus, wie würde der Quellcode aussehen an den Schnittstellen, bzw an den Stellen wo mehrere UseCases aufgerufen werden. Im Controller würde ich viele parameter an ein UseCase übergeben oder paar parameter an mehrere UseCases übergeben, der Controller müsste die in jedemfall vorher irgendwie zusammen kriegen, in den Actions des Controllers würde man viel $request->get() $request->post() $request->getSession()->getBag()->get() sehen usw. bei den Tests hätte man das gleiche.
    Wie schon geschrieben, naive Klassen. Kein Request, kein Response. Welche Parameter ich von einem Webrequest bekomme, definiere ich in meinem Router. Ich bekomme die als skalare Parameterwerte. Oder wenn es komplexere Objekte sind, dann bekomme ich ein Array. Ggf muss ich hier noch etwas Gluecode dranflanschen, damit ich bei der weiteren Verarbeitung wieder ein normales Entity-Objekt habe. Die Session ist eine Dependency der jeweiligen Klasse. So kann jede Klasse eine eigene, isolierte Session haben.

    Zitat von BlackScorp Beitrag anzeigen
    Jetzt neben den Parametern müsste ich noch entities an das UseCase übergeben, d.h. im Controller müsste ich dann mir das Repository schnappen, findBy methoden aufrufen, entities an useCase übergeben, genauso wäre es bei den Tests. Wir hätten am Ende große Controller, große Tests. Aktuell sind meine Tests/Controller i.d.r dreizeiler.
    Damit machst du nichts anderes, als dass du die Zuständigkeiten deiner Klassen weiter auftrennst. Das mag in einigen Fällen vernünftig sein. Was aber, wenn der Controller nun mal viele Konfigurationsmöglichkeiten zulässt? Sortierung, Filterung, Paginierung, etc.
    Wie abstrahierst du das weg?
    Sicher wäre es sauber zu sagen, Ja, dafür habe ich dediziertes Request-Objekt, dass einfach nur die selektierten Daten ausgibt und sich selbst um die komplexen Filter-Schritte kümmert. Was bleibt? Genau, ich kann den Controller jetzt mit 3 Zeilen Testcode statt 20 Zeilen testen. Allerdings brauche ich bei einem Request jetzt die 18 Zeilen und ich habe mindestens zwei Dateien statt einer.

    Mal ehrlich. Wenn ich ein Produkt wie Magento entwickel, dann verstehe ich den Aufwand. Wenn die Anwendung nur für meine Firma ist, dann ist das rausgeschmissenes Geld.

    Kommentar


    • #32
      Zitat von rkr Beitrag anzeigen

      Aha. Egal wie komplex ein Problem auch ist... Wenn ich es in einen UseCase packe ist es immer zeitlich berechenbar.
      Wenn ich ein komplexes Problem habe, dann muss ich dieses Problem in Teilprobleme zerlegen. Und zwar solange, bis ein Teil-Problem nicht mehr komplex genug ist, um es weiter zu zerlegen. Das Prozedere lässt sich gut mit einer Mindmap darstellen und die benötigte Zeit entsprechend berechnen. Deine Teilschritte auf oberster Ebene sind jeweils immer UseCases. Darunter eine Mischung aus irgendwie verdrahteten Services (soweit ich das jetzt verstanden habe).
      Tendentiell sind die "UseCases" bei nicht-trivialen Problemen von der eigentlichen Problemlösung mindestens 2 Ebenen entfernt - jedenfalls bei mir. Bei mir sind es Sub-Controller bzw. Composite Services.

      Hier mal ein relativ oberflächlicher Prozess um Bestellungen einem Marktplatz verschiedener Clients zu beziehen:



      Wie teilst du diesen Prozess in deinem Modell auf?
      diese ganzen geschichten mit "Trigger event , mach update hier und update da" das sind technische anforderungen, die wären erst garnicht im UseCase drin, UseCase "Fetch new Orders for all Clients" würde eben aus dem Repository OrderEntities auslesen, das wars.

      Zitat von rkr Beitrag anzeigen
      Dieser Idealvorstellung hing ich noch mal nach. Irgendwann empfand ich es als lästig, Tests für alles zu schreiben. Triviale Dinge zu testen, ist ziemlich wertlos. Aber egal, das verfremdet das Thema zu sehr
      ich schreibe nicht für alles tests, nur für UseCases , implementierungen, Bugs ich versuche so viel es geht zu testen denn ich bin des öffteren auf die Nase gefallen.

      Zitat von rkr Beitrag anzeigen
      Wie schon geschrieben, naive Klassen. Kein Request, kein Response. Welche Parameter ich von einem Webrequest bekomme, definiere ich in meinem Router. Ich bekomme die als skalare Parameterwerte. Oder wenn es komplexere Objekte sind, dann bekomme ich ein Array. Ggf muss ich hier noch etwas Gluecode dranflanschen, damit ich bei der weiteren Verarbeitung wieder ein normales Entity-Objekt habe. Die Session ist eine Dependency der jeweiligen Klasse. So kann jede Klasse eine eigene, isolierte Session haben.
      Und dann innerhalb des Controllers hast du den Array und musst überlegen welche keys es hatte

      Zitat von rkr Beitrag anzeigen
      Damit machst du nichts anderes, als dass du die Zuständigkeiten deiner Klassen weiter auftrennst. Das mag in einigen Fällen vernünftig sein. Was aber, wenn der Controller nun mal viele Konfigurationsmöglichkeiten zulässt? Sortierung, Filterung, Paginierung, etc.
      Wie abstrahierst du das weg?
      https://en.wikipedia.org/wiki/Criteria_Pattern

      Zitat von rkr Beitrag anzeigen
      Mal ehrlich. Wenn ich ein Produkt wie Magento entwickel, dann verstehe ich den Aufwand. Wenn die Anwendung nur für meine Firma ist, dann ist das rausgeschmissenes Geld.
      mein Ziel ist es, wenn in 20 Jahren irgend jemand mein Projekt erbt, soll er mit hilfe der tests wissen dass er nichts kaputtgemacht hat ohne jedes einzelne feature des Projektes zu kennen.

      es mag sein dass es an einigen stellen ein wenig too much ist, jedoch setze ich es konsequent durch wegen der routine
      apt-get install npm -> npm install -g bower -> bower install <package> YOLO [URL]https://www.paypal.me/BlackScorp[/URL] | Mein Youtube PHP Kanal: [url]https://www.youtube.com/c/VitalijMik[/url]

      Kommentar


      • #33
        Zitat von BlackScorp Beitrag anzeigen
        diese ganzen geschichten mit "Trigger event , mach update hier und update da" das sind technische anforderungen, die wären erst garnicht im UseCase drin, UseCase "Fetch new Orders for all Clients" würde eben aus dem Repository OrderEntities auslesen, das wars.
        Und was willst du da testen?

        Zitat von BlackScorp Beitrag anzeigen
        Und dann innerhalb des Controllers hast du den Array und musst überlegen welche keys es hatte
        Das weiß der Controller nicht. Das weiß vielleicht das Formular-Objekt oder ein Hydrator. Der Controller weiß nur, wer (im Sinne von Interface) damit etwas anfangen kann. Evtl. wird der Controller zu einem Gluecontroller, der das Objekt zusammensetzten lässt und es an einen anderen Controller weiterdelegiert.

        Ich meinte das im Kontext deiner Testkomplexität.

        Kommentar


        • #34
          Zitat von rkr Beitrag anzeigen
          Und was willst du da testen?
          wenn ich die implementation für das Repository baue, würde ich dann es dann testen, es gibt einige interessante test pattern auf cleancoders.com, für events werden zum Beispiel Spy Objekte verwendet die biem auslösen eines events irgendwie ein zähler hochzählen

          Zitat von rkr Beitrag anzeigen
          Das weiß der Controller nicht. Das weiß vielleicht das Formular-Objekt oder ein Hydrator. Der Controller weiß nur, wer (im Sinne von Interface) damit etwas anfangen kann. Evtl. wird der Controller zu einem Gluecontroller, der das Objekt zusammensetzten lässt und es an einen anderen Controller weiterdelegiert.


          Ich meinte das im Kontext deiner Testkomplexität.
          Der test wäre relativ einfach, weil in PHPUnit hast du ja https://phpunit.de/manual/current/en...data-providers data providers, dieser würde zb requests übergeben in allen möglichen konstellationen und zu jedem request würde ich das zu erwatete response übergeben so dass der test etwa so aussehen würde

          PHP-Code:
          /**
          * @dataProvider Foo\Bar\Class:method
          */
          public function testFilter($request,$expectedResponse){
          $actualResponse $this->executeUseCase($request);
          $this->assertEquals($actualResponse,$expectedResponse) ;

          apt-get install npm -> npm install -g bower -> bower install <package> YOLO [URL]https://www.paypal.me/BlackScorp[/URL] | Mein Youtube PHP Kanal: [url]https://www.youtube.com/c/VitalijMik[/url]

          Kommentar


          • #35
            Zitat von tr0y Beitrag anzeigen
            Moderation: Thema gepinnt.

            Es wäre schön wenn du im Start-Post ein paar einleitende Sätze zum Thema Clean Code verlieren würdest.
            Ist das Thema noch aktuell? Dieses Jahr (2017) noch kein einziger Beitrag. Schade. Dem Moderator kann ich nur zustimmen.
            The one thing worse than tight coupling is lousy coupling. -- SRP OCP LSP ISP DIP

            Kommentar


            • #36
              Weiß nicht, ob das Thema in der aktuellen Form heute noch gepinnt gehört.
              Das Thema an sich ist sehr wichtig, aber nur ein Teil dessen, was für qualitativ gute Software notwendig ist. Daneben halte ich DI, TDD und Basiswissen rund um Funktionale Konzepte für essentiell wichtig, um Softwarequalität 2017/2018 ausreichend zu vermitteln.

              Kommentar


              • #37
                Also das Thema Clean Code ist jetzt nichts von 2017 sondern schon wesentlich älter.

                https://www.amazon.de/Clean-Code-Han...dp/0132350882/ 2008 kam das erste Buch auf Amazon raus. DI und Basiswissen um Funktionale Konzepte sind ja erweiterungen zum Clean Code und nicht ersatz.

                MrScoville

                ich dachte, ich hätte in anderen Antworten hier geschrieben, wieso und weshalb, hast du eine Konkrete Frage? ansonsten 42

                LG
                apt-get install npm -> npm install -g bower -> bower install <package> YOLO [URL]https://www.paypal.me/BlackScorp[/URL] | Mein Youtube PHP Kanal: [url]https://www.youtube.com/c/VitalijMik[/url]

                Kommentar


                • #38
                  Wie gehtst du in der Form eigentlich mit Exceptions um? Kappselst du die auf jeder Ebene?

                  Kommentar


                  • #39
                    Zitat von erc Beitrag anzeigen
                    Wie gehtst du in der Form eigentlich mit Exceptions um? Kappselst du die auf jeder Ebene?
                    Ne, kommt drauf an, ich schreibe ja erst die Tests, und in den Tests wird dann auf die Exceptions geschaut
                    apt-get install npm -> npm install -g bower -> bower install <package> YOLO [URL]https://www.paypal.me/BlackScorp[/URL] | Mein Youtube PHP Kanal: [url]https://www.youtube.com/c/VitalijMik[/url]

                    Kommentar


                    • #40
                      Ich weiß nicht ob wir aneinander vorbeireden. Ich meine Z.B.:

                      class createBlaUseCase
                      --class blablaRepository
                      ----class blablaPersistentWebservice
                      -----class SoapClient

                      Beim erzeugen der Instanzen und zusammenfügen ist alles OK. Beim Ausführen von

                      PHP-Code:
                      $create_bla_use_case->run(new createBlaUseCaseRequest(...)); 
                      gibt es eine Exception im SoapClient. Solange die Exception nicht gefangen werden muss, ist es egal was für eine Exception auftritt.
                      Jetzt aber angenommen ich habe ein Fallback für createBlaUseCase z.B. createBlaFallbackUseCase. Dann muss die Exception gefangen werden. Ein Fangen von \Exception ist sehr unschön und das Fangen von SoapClientException würde eine Abhängigkeit über 4 Ebenen bedeuten. Ums sauber zu haben, müssten die Exceptions auf jeder Ebene neu verpackt werden. Machst du das oder gibt es andere Ansätze?

                      Kommentar


                      • #41
                        Ich würde wahrscheinlich eine Klasse "FallbackSoapClient" erzeugen und da drin würde ich den SoapClient aufrufen und bei Exception den Fallback aufrufen.

                        Von der Sicht des UseCases aus, exestiert ClientA und ClientB nicht. Es gibt nur EIN Interface und der liefert mir bestimmte Eregebnisse. Dass da in bestimmten Fällen irgendwas mal nicht funktioniert und ein Fallback geschaltet wird, ist der Konkreten Implementeriung überlassen.
                        apt-get install npm -> npm install -g bower -> bower install <package> YOLO [URL]https://www.paypal.me/BlackScorp[/URL] | Mein Youtube PHP Kanal: [url]https://www.youtube.com/c/VitalijMik[/url]

                        Kommentar


                        • #42
                          Wenn ich erc richtig verstanden habe, geht es ihm um die Handhabung von Exceptions über mehrere voneinander isolierte Ebenen hinweg. Wenn wir beim Beispiel von erc bleiben, könnte es passieren dass SoapClient eine Exception wirft. BlaUseCase weiß aber gar nicht, dass der von ihm verwendetet Service (BlaRepository) ein paar Ebenen tiefer mit dem SoapClient arbeitet. Also entweder fängt BlaUseCase nun die SoapFault-Exception und bricht damit die Isolation der Komponenten, oder die SoapFault-Exception wird Ebene für Ebene neu verpackt: SoapFault -> PersistentWebserviceException -> RepositoryException. BlaUseCase würde nun RepositoryException fangen und verarbeiten - unabhängig davon wie das Repository arbeitet.
                          [SIZE="1"]Atwood's Law: any application that can be written in JavaScript, will eventually be written in JavaScript.[/SIZE]

                          Kommentar


                          • #43
                            Jo, aber das UseCase soll ja nicht wissen dass da eine SoapFaultException kommen könnte, das UseCase wäre an Detail gebunden, das geht nicht, du könntest auch das UseCase nicht isoliert testen.

                            Und darf das UseCase nicht entscheiden "hm.. ich nehme repo 1, wenn das dann RepositoryException wirft, nehme ich Repo 2" Diese Entscheidung findet in einem Repo statt oder man würde einen Service machen der dann die Logik hat. Keine Ahnung "BlaRepositoryProvider"
                            apt-get install npm -> npm install -g bower -> bower install <package> YOLO [URL]https://www.paypal.me/BlackScorp[/URL] | Mein Youtube PHP Kanal: [url]https://www.youtube.com/c/VitalijMik[/url]

                            Kommentar


                            • #44
                              Genau das. Ich hatte aber ein Fallback im Sinn, der nicht technisch angesiedet ist. Z.B. ein Shopsystem was Bestellungen nur über ein anderes System verwaltet. Neue Bestellungen werden per Soap an das Backend übergeben. Gibt es Fehler, ist der Fallback z.B. Mail/Dateisystem/Message Queue/oder ähnliches, aufjedenfall ein Fallback der sich grundlegend unterscheidet, auch bei Rückgabeparametertern und evtl. ein anderes Verhalten des Frontends erfordert.

                              Ich komm auf das Thema eigentlich nur, weil PhpStorm mit dem letzten Update Code Inspections aktiviert, die versuchen Checked Exceptions zu erzwingen. Entweder führt das zu Abängigkeit zwischen Ebenen die nix voneinander wissen dürfen oder jede Ebene kapselt die darunterliegende Exceptions. Es hat mich interssiert wie du das im Clean Code Ansatz machst.

                              Kommentar


                              • #45
                                Zitat von erc Beitrag anzeigen
                                Genau das. Ich hatte aber ein Fallback im Sinn, der nicht technisch angesiedet ist. Z.B. ein Shopsystem was Bestellungen nur über ein anderes System verwaltet. Neue Bestellungen werden per Soap an das Backend übergeben. Gibt es Fehler, ist der Fallback z.B. Mail/Dateisystem/Message Queue/oder ähnliches, aufjedenfall ein Fallback der sich grundlegend unterscheidet, auch bei Rückgabeparametertern und evtl. ein anderes Verhalten des Frontends erfordert.
                                Wie du das im Detail implementierst hängt von deiner Anwendung ab. Ob nun ein OrderRepository das Fallback-Verhalten intern implementiert oder du da eine Form der Composition nutzt, ist dir überlassen und für das Thema "Clean Code" auch erstmal völlig egal. Wichtig ist nur dass keine Abhängigkeiten zwischen Ebenen hergestellt werden, die eigentlich nichts voneinander wissen.
                                [SIZE="1"]Atwood's Law: any application that can be written in JavaScript, will eventually be written in JavaScript.[/SIZE]

                                Kommentar

                                Lädt...
                                X