Eure Meinung zu exzessiver Nutzung von Interfaces?

Du solltest mit deinem Frust lieber ein Feedbackgespräch mit deinem Teamleiter bzw. Vorgesetzten suchen. Wenn du willst, dass sich etwas ändert, suche ein konstruktives Gespräch.
 
Kokujou schrieb:
2. Was hat Mocken denn mit Erweiterbarkeit zutun?
https://en.wikipedia.org/wiki/Mock_object schrieb:
Mock object

In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways, most often as part of a software testing initiative.
Ein Mock ist nur ein Objekt, was ein definiertes Verhalten ausweist. In PHP kann ich anonyme Klassen erstellen, bspw.:
Code:
<?php

interface Foo
{
    public function bar() : string;
}

$mock = new class
    implements Foo
{
    public function bar() : string
    {
        return "baz";
    }
}
Das ist auch ein Mock. Und damit lässt es sich testen, weil ich Ein- und Ausgabe bestimme und das Testresultat vollkommen isoliert laufen kann, ohne dass eine externe DB angefragt wird und ich vorher Testdaten generieren muss.
Code:
interface IDownloadable
{
    void GetMetaData();
    void PingHost();
    void DownloadFiles();
    void VerifyDownload();
}
Damit versteifst du dich aber auch extrem auf diese eine Implementierung. Warum trennst du die Sachen nicht auf?
Code:
interface IMetaDataRetriever
{
  void GetMetaData();
}

interface INetworkConnectivityChecker
{
  void IsHostAlive();
}

interface IContentRetriever
{
  void GetContent();
}

interface IContentVerifier
{
  void Verify();
}

interface IDownloadable : IMetaDataRetriever,
  INetworkConnectivityChecker,
  IContentRetriever,
  IContentVerifier
{
}
So kannst du für dieses Interface eine Komposition aus mehreren Interfaces erstellen und bist flexibel.

Der IContentRetriever kann dann bspw. ein HttpContentRetriever sein, der anhand eines HttpClient Daten von einer URL lädt. Oder ein FtpContentRetriever, der Sachen von einer FTP-URL lädt oder oder oder... Das könntest du wiederum mit ner UrlContentRetrieverFactory abstrahieren, wodurch du nur die Factory aufrufen musst und die kümmert sich, ob es ein http-, ftp-, ssh-, UNC-, magnet-, ...-Link ist. Mit einen Interface wärst du extrem unflexibel und bräuchtest für jeden neue Art eine extra Klasse. So kannst du auswählen was du willst und erhälst durch "Zusammensuchen" von vier Teilen eine komplett neue Funktionalität ohne Code schreiben zu müssen. Und wenn, schreibst du eine neue Klasse und erhälst wieder viele neue Möglichkeiten. Statt bspw. den HttpContentRetriever zu nutzen, kannst du dich auch weiter spezialisieren und einen FacebookPostContentRetriever erstellen, der nur Content aus Facebook Posts lädt. Die restlichen Klassen bleiben hierbei die Selben, lediglich der ContentRetriever hat sich geändert.

Als INetworkConnectivityChecker kann es bspw. einen PingNetworkConnectivityChecker sein, der einfach nur den Hostnamen anpingt. In Kombination mit HTTP, wäre vielleicht ein HttpHeadConnectivityChecker relevant, der vorher auf einen `200`er Status prüft (bzw. != [45]xx). Ggf. ist eine Kombination aus beidem sinnvoll?

Das bekommst du alles mit deinem einen Interface nicht hin. Du versuchst im Prinzip mehrere Schritte unter einem Hut zu vereinen, was mir durch kurze Überlegungen bereits zu widerlegen gelingt.

IDownloadable sollte hierbei aber eher eine konkrete Implementierung (= Klasse) darstellen, die durch zusammenfügen von den genannten Interfaces die gewünschte Funktionalität bereitstellt. Wo wir dabei sind: Du kannst auch einfach alle vier Interfaces in einer Klasse bündeln, dagegen spricht überhaupt nichts. Aber es bringt nichts alles in nen Topf zu werfen, nur damit du dann Einzelteile der Klasse nutzt.

edit: Ganz ehrlich, wie bereits vorgeschlagen wurde: Setz dich mit nem alten Hasen hin, der soll dir das erklären, an genau diesem ganz speziellen Beispiel bei euch. Fragen kost nix und wenn er es dir erklärt und du den Sinn dahinter verstehst, hilft es allen. Such dir vielleicht auch andere Beispiele heraus, leg ihm die hin und ihr trefft euch ggf. nen Tag später. Dann wird er es dir ordentlich und im Detail erklären können.
 
Zuletzt bearbeitet:
Weil ich es besser finde klar zu definieren, wenn man schon ein Interface hat. Wenn du schon so etwas tust wie eine ganze extra Klasse, eine Extra Datei dafür sogar anzulegen sollte da zumindest was drinne stehen was weiterhilft.
Die Information dass ein Downloader-Interface downloadet ist so hilfreich wie... dafür fällt mir nichtmal etwas ein.

In einem Interface kannst du mehrere hilfreiche informationen angeben:
1. Rückgabetyp,
2. Name der Klasse
3. Parameter

vielleicht sogar noch mehr was mir jetzt nicht einfällt.

Also kannst du ganz klar definieren was eine Funktion tun soll. Wenn du also downloaden willst kannst du genaud efinieren wie der Download-Prozess ablaufen soll. Du fängst an, identifizierst z.B. das Ziel, holst dir Metadaten, schickst die vielleicht noch irgendwo hin etc...

Wenn du unbedingt andere Funktionen kannst du das auch tun a la:

public override <type> <methodName>(<params>) => <TargetMethod>();

ein Interface dass einfach nur da liegt und nichts sagt ist für meine Begrifflichkeit einfach nutzlos. Darum mag ich ja auch abstrakte Klassen. Weil du da sogar noch Funktionen definieren kannst du diese erzwungenen abstrakten Funktionen kombinieren. Du kannst deine Klasse also quasi graduell immer weiter planen. Statt dass du nur sagst sie muss das und das können kannst du in einer abstrakten Klasse auch noch sagen sie muss diese Dinge so oder so zusammen anwenden.
Ergänzung ()

achja und zum Thema Feedback mit dem Team:

Ich bin sehr zurückhaltend was solche Phrasen wie "Ich weiß nicht" "Ich verstehe nicht" oder anderen kontroversen Aussagen angeht die irgendwie indizieren dass ich anderer Meinung bin. Ich bin in der Probezeit und ich bin der Neue. Das heißt egal was ich sage und wie sehr ich recht habe oder auch nicht ich habe Prinzipiell erstmal unrecht mit allem was ich tue oder sage. Und lange Ausführungen darüber warum ich nicht deren Meinung bin führen eher zu einer schnellen Entlassung, habe ich die Befürchtung. Am Ende ist es schließlich Arbeit und es geht mir ums Geld und wenn die dort tatsächlich alle wollen dass ich nur meine Zeit verschwende dann soll es so sein.

Ich meine ich schreibe immer guten, strukturierten und lesbaren Code, sagen sie sogar selbst! aber ich krieg ständig haufenweise negative Kommentare. was ja per se nichts schlechtes ist. Mich stört nur dass wenn andere dann genau die gleichen Dinge falsch machen oder sogar noch schlimmeren Code abliefern der weder gut separiert ist oder osnst was, er einfach durchgewinkt wird weil "Hauptsache wir haben was." das ist jetzt übrigens nicht meine Aussage. Denn sie sagten sogar dass mein Code besser war als der vorher. Dann haben sie ihn aber abgelehnt weil weiß der Geier XD

Das vermittelt mir den Eindruck "halt die Klappe und tu was wir sagen. Nicht was wir tun."
 
Zuletzt bearbeitet:
Kokujou schrieb:
Ich bin in der Probezeit und ich bin der Neue. Das heißt egal was ich sage und wie sehr ich recht habe oder auch nicht ich habe Prinzipiell erstmal unrecht mit allem was ich tue oder sage. Und lange Ausführungen darüber warum ich nicht deren Meinung bin führen eher zu einer schnellen Entlassung, habe ich die Befürchtung. Am Ende ist es schließlich Arbeit und es geht mir ums Geld und wenn die dort tatsächlich alle wollen dass ich nur meine Zeit verschwende dann soll es so sein.
Also wenns bei dir so schlimm ist (ich kanns ja nicht bestätigen) würde ich mir nen besseren Arbeitgeber suchen. Ich bin jetzt seit ca. 2 Jahren bei mir im Unternehmen, hab zwar am Anfang auch Mist gebaut und nicht immer alles so schlau gelöst wie ich es hätte können, aber man hat meine Vorschläge immer ernst genommen und auf einer Augenhöhe miteinander diskutiert. Deine Aufgabe im Sinne von "na wenns alle machen mach ich es halt auch, is ja nur n Job" wirkt wie die totale Selbstaufgabe eines jungen, motivierten Mitarbeiters, der alle Prinzipien aus Hoffnungslosigkeit wegwirft. Wäre schade drum, wenns wirklich so ist.
Ich würde jedoch dennoch erstmal das Gespräch suchen, vorher wichtige Beispiele und Fakten sammeln (=> gute Vorbereitung) und dann nicht so gereizt wie hier sondern sachlich, ruhig und argumentativ mit den Beteiligten diskutieren. Die Weisheit hast du sicher auch nicht mit dem Löffel gefressen, aber das heißt auch nicht, dass Alteingesessenes deshalb automatisch sinnvoll ist.
Wenn diese grundlegende Basis der Kommunikation nicht funktioniert...wäre mir das Arbeitsklima zu blöd um ehrlich zu sein. Aber das ist ja nicht meine Entscheidung.

Als unabhängige Designidee finde ich @Yuuri's Vorschlag btw ziemlich genial. Strukturiert, flexibel, super testbar. Gefällt mir.
 
ich sag ja nicht dass ich dann definitiv gefeuert WERDE. Ich sag nur dass ich das Risiko nicht eingehen will meine Unwissenheit und mein Unverständnis auch noch zu untermalen.

Ich persönlich halte es für recht warhscheinlich dass die ganze Welt mal wieder ganz andere Kriterien hat als ich selbst und ich der Exzentriker bin.

und so wirklich nen punkt übermitteln kann man da nicht. Ich meine erst sagt man mir man ändert seinen code nicht für tests. dann fügt man nicht nur interfaces hinzu sondern baut den ganzen code inzwischen über wochen um nur weil man n paar sinnlose tests hinzufügen soll...

erst sagt man mir dass ich dinge irgendwie doppelt teste dann macht man dasselbe 20 mal und nennt es guten stil.

ich versuch irgendwie zuv erstehen wie die von mir erwarten dass ich entwickeln soll aber sie schmeißen ihren eigenen stil dauernd auf den haufen und machen was neues. Ist es jetzt vielleicht guter stil für jede Klasse ein Interface zu bauen? Ist es guter Stil 100 Tests pro Funktion einzubauen? Ist es guter Stil seine Projektmappe so vollzukleistern dass ich erstmal eine Stunde brauche um überhaupt die Einstiegsdatei für meinen Task zu finden? XD

Ich meine ich gucke durch die Solution... nein das sind die 100 interfaces... das sind die 900 Testklassen die x Testprojekte, irgendwelche Konfigurationsdateien.



Was das mit der Selbstaufgabe angeht: wenn man so ist dann ist man einfach nicht akzeptabel. Es läuft vermutlich darauf hinaus dass ich die klappe halten muss und das tun muss was die sagen. Eigentlich wollte ich mir richtig Mühe geben, weil ich ein sehr eifriger Mensch bin und auch n bisl aufsteigenw ill, besonders vom Gehalt ( ;) ). Das ganze verdammte Projekt hätte ich vermutlich in 2 Tagen hingekriegt ohne dass ein einziger Bug ist. vielleicht 4 wenn man die Tests bedenkt die ich ja auch selbst für sinnvoll halte.

Aber ich würde halt nur sinnvolle tests machen und mich aufs wesentliche konzentrieren statt dauernd über offensichtlichkeiten zu diskutieren. und was das bauen guter und cleverer algorithmen angeht bezeichne ich mich als sehr gut. Aber natürlich beitet es absolut keine Aufstiegschancen wenn man den ganzen Tag nur die selben3 Testzeilen für seine Funktionen wiederholt oder dauernd nur sinnlose interfaces schreibt und dann vielleicht einmal die woche was macht was das projekt tatsächlich weiterbringt. Aber da hier auch jeder der meinung ist dass das "gut" und "sinnvoll" und "standard" ist, ist es wohl mal wieder einer der Fälle dass ich der totale exzentriker mit völlig absurden ansichten ist.

Am Ende geht es weder darum dass es einem Gefällt oder dass es einem Spaß macht, oder dass es einen erfüllt oder sonst was. Es ist und bleibt arbeit. Man wird bezahlt dafür dass man tut was gefordert wird. und wenn hier auch alle der meinung sind dass meine meinung absolut unbegründet und "mist" ist, dann kann ich mich auch nicht wirklich beschweren.

Wer weiß vielleicht wird ja irgendwann ein Test-Sklave eingestellt der als coder nur für das Schreiben von Tests zuständig ist ;) Und Leute wie ich sind dann sowas wie code-Architekten die die ganze Funktionalität herstellen und das projekt tatsächlich weiterbringen :P
 
Schwierig von außen zu beurteilen wo nun das Problem liegt. Ich schildere dir einfach mal warum ich recht viel von Tests halte und wie ich bei diesen Vorgehe.

Wir haben über 600 Tests in unserem Projekt, aber diese decken längst nicht den ganzen Code ab. Warum? Ganz einfach: Wir haben nicht von Anfang an Tests geschrieben (leider!):D

Und ich fluche jedes mal, wenn ich den alten ungetesteten Code anpacken muss. Jede Änderung am Code hat das Potenzial etwas kaputtzumachen. Mit Tests habe ich die Sicherheit, dass das nicht passiert.
Ich pflege an dieser Stelle dann auch Tests nach. Das geht mal einfach und mal ist es extrem schwer Tests nachträglich einzubauen.
Meiner Meinung nach ist es ein Zeichen von schlechtem Code, wenn es nur mit erheblichem Aufwand möglich ist diesen zu testen.

Testen tue ich, aber auch nur da wo es Sinn ergibt. Und das sind auf jeden Fall auch solche Fälle wo nur geprüft wird, ob eine Funktion mit den richtigen Argumenten aufgerufen wird. Bei mir gibt es keinen Code, wenn es keinen Test dafür gibt.

Beispiel:

Ich habe ein Modul, das eine Datei einliest/parst und anschließend aufwendig verarbeitet.


Nun schreibe ich einen Test, der die Verarbeitung prüft.
Damit der Test aber nicht >5 Sekunden Ausführungszeit hat, teile ich die Aufgaben.
Sprich, es gibt ein Interface, das eine Methode 'LoadFile(filePath)' hat und irgendein Objekt zurückgibt.
Dieses Interface mocke ich nun in meinem Test und prüfe, dann natürlich auch, ob das Interface mit den korrekten Argumenten aufgerufen wird.

Wenn ich damit fertig bin, teste ich dann noch die Implementation des Interfaces. Da wird dann das File eingelesen und geprüft ob dieses richtig eingelesen wird.
So ein Parser kann schnell komplex werden und auch entsprechend umfangreich. Am Ende wird die Funktionalität vermutlich in dutzende Klassen aufgeteilt sein.
Schreibe ich nun für jede Klasse ein Interface und teste die Interaktion (also die Funktionsaufrufe) untereinander?
Nein. Ich habe einen Test, der genau meine Anforderung prüft, warum soll ich nun Testen wie ich das implementiere? Das hat nur den Nachteil, das ein mögliches Refactoring erschwert wird → Test hindert mich daran den Code sauber zu halten

Was ich damit sagen möchte: Ich teste nur dort mit Mocks wo es Sinn macht. ZB teste ich nicht mit einer echten Datenbank, sondern mit einem Mock dieser.


Dennoch habe ich lieber zu viele Tests als zu wenige.
Wenn nur ein Teil abgeprüft wird, ist es als ob ich keinen Test habe → Ich muss in der Implementation schauen, was die Anforderungen sind → ätzend

Und darauf zu vertrauen, dass in Pull-Requests ein Mitarbeiter sieht, ob eine Methode falsch aufgerufen wird, halte ich für fahrlässig. Warum nicht einen Test dafür schreiben, der das in 0.001 Sekunden erledigt? Spart Zeit und Geld

Code und Anforderungen ändern sich ständig und müssen dementsprechend angepasst werden. Bei einer großen Codebase ist das ohne automatische Tests kaum möglich.

Auch wenn der Rockstar-Programmierer das Projekt in 2 Wochen ohne Tests anstatt in 4 Wochen mit Tests geschrieben hätte, seit ihr auf lange Sicht viel langsamer.
→ Alles manuell testen
→ Kaum Refactoring möglich, da keine Sicherheit, dass man nichts kaputt macht
→ Codebase gammelt immer weiter vor sich hin

Ich sage nicht, dass ein Projekt ohne Tests zum Scheitern verurteilt ist.
Aber für mich ist TDD eine effektive Methode, dafür zu sorgen, dass das Projekt wartbar bleibt.
 
Gojira schrieb:
Auch wenn der Rockstar-Programmierer das Projekt in 2 Wochen ohne Tests anstatt in 4 Wochen mit Tests geschrieben hätte, seit ihr auf lange Sicht viel langsamer.
→ Alles manuell testen
→ Kaum Refactoring möglich, da keine Sicherheit, dass man nichts kaputt macht
→ Codebase gammelt immer weiter vor sich hin
I know that feeling. Wir haben zwar ein paar Tests, aber da mancher Code seit zig Jahren einfach weiter entwickelt wird und nie nach TDD entwickelt wurde weiß man selten oder nur mit Recherche, was wirklich irgendwie mit anderen Sachen kollidiert. Manche Sachen fasst man einfach nicht an, weil man die Auswirkungen nur erahnen kann. Aber wir haben auch nicht genug Leute um die Tests alle nachzupflegen und nebenher damit zu entwickeln. Wenn ich könnte...würde ich wohl auch TDD verfolgen :D Finde es auch jeden Fall super, wenn sich Leute das zur Aufgabe machen. Kann die Aussage nur so unterschreiben!
 
  • Gefällt mir
Reaktionen: new Account()
Gojira schrieb:
Und ich fluche jedes mal, wenn ich den alten ungetesteten Code anpacken muss. Jede Änderung am Code hat das Potenzial etwas kaputtzumachen. Mit Tests habe ich die Sicherheit, dass das nicht passiert.
Ach wirklich ;) ganz ehrlich uns ist es schon 100mal passiert dass wir etwas kaputt gemacht haben. warum? Weil tests eben auch nicht 100%ig sind. entweder ist so viel weggemockt, dass es immer grün wird egal was man sonst noch tut, oder es wird halt immer rot auch wenn deine changes valide sind. Egal was man tut am Ende bleibt dasselbe ergebnis: wenn du auch nur ein Zeichen änderst musst du neu testen.

Gojira schrieb:
Auch wenn der Rockstar-Programmierer das Projekt in 2 Wochen ohne Tests anstatt in 4 Wochen mit Tests geschrieben hätte, seit ihr auf lange Sicht viel langsamer.
→ Alles manuell testen
→ Kaum Refactoring möglich, da keine Sicherheit, dass man nichts kaputt macht
→ Codebase gammelt immer weiter vor sich hin
Ich sag ja nicht dass man GAR nicht testen muss. Aber man testet meiner meinung nach eher halbwegs komplexe Funktionen als Unit tests und den Rest auf Integration Test ebene. GAR nicht zu testen ist natürlich eine Dummheit die sich bei meinem Master-Projekt,, wo ich noch nichtmal wusste dass es sowas wie Tests gibt (ugh...) rächt. Obwohl ehrlich gesagt bin ich auch da noch ganz gut klar gekommen. Tests hätten mir voraussichtlich nur die 5 Minuten erspart die damit endete: "uff warum hab ich idiot das nicht gleich gemerkt." und das war abzählbar gering.

Refactoring ist immer möglich, du brauchst keine Unit tests um zu sehen wenn dir was zerschossen wird. Viele Folgefehler zeigt dir der Compiler an und als Programmierer mit mindestens durchschnittlichen Fähigkeiten sollte es dir auch möglich sein einen Code zu refactoren der ungetestet ist und wo nicht gleich alles zu bruch geht. Schließlich sollte man doch wissen wie man die Abläufe im Code nachvollzieht. und wenn man dann in einem Test höheren Levels eine Meldung kriegt "Exception" dann sollte man an Hand der Art der Exception eigentlich auch wissen an welchen deiner Changes der Fehler liegt,, um ihn zu verbessern. Abgesehen davon ist es auch eine Anforderung guten Codes, dass gut ersichtlich ist was er tut und wann er aufs Maul fliegen wird.

Klar, ein bloßer E2E-Test reicht nicht. Der würde Minuten dauern allein um durchzulaufen und nichtmal ansatzweise die Fehlermeldungen bringen die uns weiterhelfen, es sei denn man baut exzessives Logging ein und das ist ja auch nicht sinn der Sache.

Aber Integration-Tests liefern unter umständen doch etwas mehr einblick und ganz ehrlich, wenn man in einem Team erstmal jede silbe doppelt lesen muss aus angst dass jemand trollt und das ganze Projekt "zer-refactort" dann ist Teamarbeit gleich mal gar nicht möglich.



Ich hab je länger ich an dem Projekt arbeite immer mehr den Eindruck dass Tests absolut keinen nutzen haben. Ich meine nach wie vor ist es mir noch NIE passiert, dass mir ein Test mal gezeigt hat "ey, da läuft was falsch.". Stattdessen haben wir nach wie vor die Probleme, die Tests eigentlich verhinder sollen. Kürzlich z.B. hab ich eine Stunde mit der Suche nach einer Exception verbracht, die dadurch verursacht wurde, dass jemand meinte "hey, den Parameter brauchen wir nicht, also geben wir mal null rein." ... ... ... warum das bei keinem Pull-request aufgefallen ist ist mir schleierhaft denn wenn irgendwo null steht wäre ich per se schonmal stutzig. Aber es war ja nicht so als gäbe es keinen Test dafür. Aber wie bereits gesagt war das auf einer Ebene wo wir alle Funktionalität einfach wegmocken. Das heißt er hat gar nicht mitgekriegt dass da null verarbeitet wird. Er hat es zwar als argument gekriegt aber alles weggemockt was da hätte die null verarbeiten können. Also pustekuchen.

Mich würds nicht wundern wenn vorher im Test sogar ein null-check war und jemand dachte "ich will da jetzt aber null drin haben also änder ich ihn."



Ein idealer Test wäre für mich etwas dass von anfang bis ende nicht geändert wird, es sei denn die funktion fliegt raus. und rausfliegen heißt auch so exzessiv "refactort" werden, dass die grundfunktionalität nicht mehr wiederzuerkennen ist.

Wenn man eine Funktion hat "LoadFile", dann erwartet man dass man einen FilePath reingibt und einen validen und beschreibbaren FileStream rauskriegt. Da testet man nicht erstmal jeden validen Dateipfad durch ob am Ende auch die datei geöffnet wird die man will oder so. Dafür gibts externe Bibliotheken, die wurden schon getestet. Man kann es sich auch sparen zu testen ob diese externen Bibliotheken aufgerufen worden. Man nimmt die Eingabe macht sowas wie Assert.True(x.Exists) und am Ende vielleicht noch FileStream.CanRead/CanWrite oder so.

Und wenn ich ne Config-Klasse baue will ich auch nicht jede einzelne Variable durchtesten müssen, weil die verschiedene Typen haben und in ner configs nur strings drinne stehen. Wenn du in URL konvertierst testest du die konvertierung, wenn du in int konvertierst dann eben die. und wenn du gar keine benutzerdefinierte konvertierung machst sondern nur sowas wie new Uri(x) machst kannst du dir die tests direkt sparen. schließlich soll das ding ja hinfallen wenn die config nicht richtig ist und wenn irgendjemand meint was falsches in die confi schreiben zu müssen dann ist das ja auch nicht schuld der funktion wenn ne exception fliegt. der Uri-Konstruktor ist bereits getestet. Und gucken, dass die Variable auch als Uri rauskommt und nicht plötzlich als string ist auch sinnlos. Alles was man testen muss ist eigentlich, dass wenn du ein config-File lädst und jede Variable der output-klasse abrufst du einen Wert ungleich null kriegst und keine exception fliegt. und natürlich wenn du irgendeine höhere Logik beim konvertieren drin hast wie benutzerdefiniertes parsing dass das halt richtig funktioniert.
 
Zurück
Oben