it-swarm.com.de

Was sollte Vorrang haben: YAGNI oder Good Design?

An welchem ​​Punkt sollte YAGNI Vorrang vor guten Codierungspraktiken haben und umgekehrt? Ich arbeite an einem Projekt bei der Arbeit und möchte meinen Mitarbeitern langsam gute Codestandards vorstellen (derzeit gibt es keine und alles wird nur ohne Reim oder Grund zusammen gehackt), aber nachdem ich eine Reihe von Klassen erstellt habe (wir Ich mache einen Schritt zurück und dachte, dass es gegen YAGNI verstößt, weil ich ziemlich sicher weiß, dass wir einige dieser Klassen nicht erweitern müssen.

Hier ist ein konkretes Beispiel dafür, was ich meine: Ich habe eine Datenzugriffsschicht, die eine Reihe gespeicherter Prozeduren umschließt und ein rudimentäres Muster im Repository-Stil mit grundlegenden CRUD-Funktionen verwendet. Da es eine Handvoll Methoden gibt, die alle meine Repository-Klassen benötigen, habe ich eine generische Schnittstelle für meine Repositorys mit dem Namen IRepository erstellt. Ich habe dann jedoch eine "Marker" -Schnittstelle (d. H. Eine Schnittstelle, die keine neuen Funktionen hinzufügt) für jeden Repository-Typ (z. B. ICustomerRepository) erstellt, und die konkrete Klasse implementiert dies. Ich habe dasselbe mit einer Factory-Implementierung gemacht, um die Geschäftsobjekte aus DataReaders/DataSets zu erstellen, die von der gespeicherten Prozedur zurückgegeben werden. Die Signatur meiner Repository-Klasse sieht ungefähr so ​​aus:

public class CustomerRepository : ICustomerRepository
{
    ICustomerFactory factory = null;

    public CustomerRepository() : this(new CustomerFactory() { }

    public CustomerRepository(ICustomerFactory factory) {
        this.factory = factory;
    }      

    public Customer Find(int customerID)
    {
        // data access stuff here
        return factory.Build(ds.Tables[0].Rows[0]);
    }
}

Ich mache mir hier Sorgen, dass ich gegen YAGNI verstoße, weil ich mit 99% iger Sicherheit weiß, dass es niemals einen Grund geben wird, diesem Repository etwas anderes als ein konkretes CustomerFactory zu geben. Da wir keine Komponententests haben, brauche ich kein MockCustomerFactory oder ähnliches, und so viele Schnittstellen könnten meine Mitarbeiter verwirren. Auf der anderen Seite scheint die Verwendung einer konkreten Implementierung der Fabrik ein Designgeruch zu sein.

Gibt es einen guten Weg, um einen Kompromiss zwischen korrektem Software-Design und nicht übermäßiger Architektur der Lösung zu erzielen? Ich frage mich, ob ich alle "einzelnen Implementierungsschnittstellen" haben muss oder ob ich ein bisschen gutes Design opfern könnte und nur zum Beispiel die Basisschnittstelle und dann die einzelne konkrete habe, und mich nicht um die Programmierung auf die Schnittstelle, wenn die Implementierung ist, die jemals verwendet wird.

78
Wayne Molina

Gibt es einen guten Weg, um einen Kompromiss zwischen korrektem Software-Design und nicht übermäßiger Architektur der Lösung zu erzielen?

YAGNI.

Ich könnte ein bisschen gutes Design opfern

Falsche Annahme.

und die Basisschnittstelle und dann der einzelne Beton,

Das ist kein "Opfer". Das ist gutes Design.

73
S.Lott

In den meisten Fällen führt das Vermeiden von Code, den Sie nicht benötigen, zu besser Design. Das wartbarste und zukunftssicherste Design verwendet nur die geringste Menge an gut benanntem, einfachem Code, der die Anforderungen erfüllt.

Die einfachsten Designs sind am einfachsten zu entwickeln. Nichts kann die Wartbarkeit so beeinträchtigen wie nutzlose, überentwickelte Abstraktionsschichten.

78

YAGNI und SOLID (oder eine andere Entwurfsmethode) schließen sich nicht gegenseitig aus. Sie sind jedoch nahezu polare Gegensätze. Sie müssen sich auch nicht zu 100% daran halten, aber es wird etwas geben -and-take; Je mehr Sie sich ein stark abstrahiertes Muster ansehen, das von einer Klasse an einem Ort verwendet wird, und YAGNI sagen und es vereinfachen, desto weniger SOLID wird das Design. Das Gegenteil kann sein Auch wahr, viele Male in der Entwicklung wird ein Design SOLID "im Glauben" implementiert, man sieht nicht, wie man es braucht, aber man hat nur eine Ahnung. Dies könnte wahr sein (und wird es immer wahrscheinlicher wahr, je mehr Erfahrung Sie sammeln), aber es könnte Sie auch in so viel technische Schulden stecken wie ein Slap-Dash-Ansatz "Do it Light"; anstelle einer DIL-Codebasis "Spaghetti-Code" erhalten Sie möglicherweise "Baklava-Code". Wenn Sie so viele Ebenen haben, dass das Hinzufügen einer Methode oder eines neuen Datenfelds zu einem tagelangen Prozess wird, bei dem Sie mit nur einer Implementierung durch Service-Proxys und lose gekoppelte Abhängigkeiten waten. Oder Sie könnten en d up mit "Spaghetti-Os-Code", der in so kleinen, locker strukturierten Teilen geliefert wird, dass Sie sich durch 50 Methoden mit jeweils 3 Zeilen nach oben, unten, links oder rechts in der Architektur bewegen.

Ich habe es in anderen Antworten gesagt, aber hier ist es: Lass es beim ersten Durchgang funktionieren. Beim zweiten Durchgang mache es elegant. Beim dritten Durchgang mache es FEST.

Aufschlüsselung:

Wenn Sie zum ersten Mal eine Codezeile schreiben, muss diese einfach funktionieren. An diesem Punkt ist es, soweit Sie wissen, einmalig. Sie erhalten also keine Stilpunkte für den Bau einer "Elfenbeinturm" -Architektur, um 2 und 2 hinzuzufügen. Tun Sie, was Sie tun müssen, und nehmen Sie an, dass Sie es nie wieder sehen werden.

Wenn Ihr Cursor das nächste Mal in diese Codezeile fährt, haben Sie Ihre Hypothese von Anfang an widerlegt. Sie überarbeiten diesen Code erneut, um ihn entweder zu erweitern oder anderweitig zu verwenden. Es handelt sich also nicht um einen einmaligen Code. Nun sollten einige Grundprinzipien wie DRY (wiederholen Sie sich nicht)) und andere einfache Regeln für das Code-Design implementiert werden: Extrahieren Sie Methoden und/oder bilden Sie Schleifen für wiederholten Code, extrahieren Sie Variablen für allgemeine Literale oder Ausdrücke, vielleicht einige Kommentare hinzufügen, aber insgesamt sollte sich Ihr Code selbst dokumentieren. Jetzt ist Ihr Code gut organisiert, wenn auch immer noch eng miteinander verbunden, und jeder andere, der ihn betrachtet, kann leicht lernen, was Sie tun, indem er den Code liest Code, anstatt ihn Zeile für Zeile zu verfolgen.

Wenn Ihr Cursor zum dritten Mal diesen Code eingibt, ist dies wahrscheinlich eine große Sache. Sie erweitern es entweder noch einmal oder es ist an mindestens drei verschiedenen anderen Stellen in der Codebasis nützlich. Zu diesem Zeitpunkt ist es ein Schlüsselelement Ihres Systems, wenn nicht sogar ein Kernelement, und sollte als solches aufgebaut sein. Zu diesem Zeitpunkt haben Sie normalerweise auch das Wissen darüber, wie es bisher verwendet wurde, sodass Sie gute Entwurfsentscheidungen hinsichtlich der Gestaltung des Entwurfs treffen können, um diese und alle neuen Verwendungen zu optimieren. Jetzt sollten die Regeln SOLID) in die Gleichung eingehen, Klassen mit Code für bestimmte Zwecke extrahieren, gemeinsame Schnittstellen für Klassen mit ähnlichen Zwecken oder Funktionen definieren, lose gekoppelte Abhängigkeiten zwischen Klassen einrichten und entwerfen die Abhängigkeiten, so dass Sie sie einfach hinzufügen, entfernen oder austauschen können.

Sollten Sie diesen Code von diesem Punkt an weiter erweitern, neu implementieren oder wiederverwenden müssen, ist alles gut verpackt und abstrahiert im "Black Box" -Format, das wir alle kennen und lieben. Schließen Sie es an einer anderen Stelle an oder fügen Sie eine neue Variante des Themas als neue Implementierung der Schnittstelle hinzu, ohne die Verwendung dieser Schnittstelle ändern zu müssen.

64
KeithS

Anstelle von beiden bevorzuge ich WTSTWCDTUAWCROT?

(Was ist das Einfachste, was wir tun können, das nützlich ist und das wir am Donnerstag veröffentlichen können?)

Einfachere Akronyme stehen auf meiner Liste, aber sie haben keine Priorität.

YAGNI und gutes Design stehen nicht im Widerspruch. Bei YAGNI geht es darum, zukünftige Bedürfnisse (nicht) zu unterstützen. Bei gutem Design geht es darum, transparent zu machen, was Ihre Software tut im Moment und wie sie dies tut.

Wird die Einführung einer Fabrik Ihren vorhandenen Code vereinfachen? Wenn nicht, fügen Sie es nicht hinzu. Wenn dies beispielsweise beim Hinzufügen von Tests der Fall ist (was Sie tun sollten!), Fügen Sie es hinzu.

Bei YAGNI geht es um ohne Hinzufügen Komplexität, um zukünftige Funktionen zu unterstützen.
Bei gutem Design geht es um Entfernen Komplexität, während alle aktuellen Funktionen unterstützt werden.

25
Jaap

Sie stehen nicht in Konflikt, Ihre Ziele sind falsch.

Was versuchst du zu erreichen?

Sie möchten hochwertige Software schreiben und dazu möchten Sie Ihre Codebasis klein halten und keine Probleme haben.

Jetzt erreichen wir einen Konflikt. Wie decken wir alle Fälle ab, wenn wir keine Fälle schreiben, die wir nicht verwenden werden?

So sieht Ihr Problem aus.

enter image description here  (jeder Interessierte, das nennt man verdunstende Wolken )

Also, was treibt das an?

  1. Sie wissen nicht, was Sie nicht brauchen werden
  2. Sie möchten keine Zeit verschwenden und Ihren Code aufblähen

Welche davon können wir lösen? Nun, es sieht so aus, als ob man keine Zeit verschwenden möchte und Code aufblähen ist ein großartiges Ziel und macht Sinn. Was ist mit dem ersten? Können wir herausfinden, was wir zum Codieren benötigen?

Ich arbeite an einem Projekt bei der Arbeit und möchte meinen Mitarbeitern langsam gute Codestandards vorstellen (derzeit gibt es keine und alles wird nur irgendwie ohne Reim oder Grund zusammen gehackt). [...] Ich trat einen Schritt zurück und dachte, es verstößt gegen YAGNI, weil ich ziemlich sicher weiß, dass wir einige dieser Klassen nicht erweitern müssen.

Lassen Sie uns das alles neu formulieren

  • Keine Codestandards
  • Keine Projektplanung läuft
  • Cowboys machen überall ihr eigenes verdammtes Ding (und du versuchst Sheriff im wilden Westen zu spielen), yee haw.

Gibt es einen guten Weg, um einen Kompromiss zwischen korrektem Software-Design und nicht übermäßiger Architektur der Lösung zu erzielen?

Sie brauchen keinen Kompromiss, Sie brauchen jemanden, der das Team leitet, der kompetent ist und eine Vision für das gesamte Projekt hat. Sie brauchen jemanden, der planen kann, was Sie brauchen werden, anstatt dass jeder von Ihnen Dinge einwirft, die Sie NICHT brauchen werden, weil Sie sich über die Zukunft so unsicher sind, weil ... warum? Ich sage dir warum, weil niemand unter euch allen einen verdammten Plan hat. Sie versuchen, Codestandards einzuführen, um ein völlig separates Problem zu beheben. Ihr Hauptproblem, das Sie lösen müssen, ist eine klare Roadmap und ein klares Projekt. Sobald Sie das haben, können Sie sagen, "Codestandards helfen uns, dieses Ziel als Team effektiver zu erreichen", was die absolute Wahrheit ist, aber außerhalb des Rahmens dieser Frage liegt.

Holen Sie sich einen Projekt-/Teammanager, der diese Dinge tun kann. Wenn Sie eine haben, müssen Sie sie nach einer Karte fragen und das YAGNI-Problem erklären, das darin besteht, dass keine Karte vorhanden ist. Wenn sie grob inkompetent sind, schreiben Sie den Plan selbst und sagen Sie: "Hier ist mein Bericht für Sie über Dinge, die wir brauchen. Bitte überprüfen Sie ihn und teilen Sie uns Ihre Entscheidung mit."

16
Incognito

Die Frage stellt ein falsches Dilemma dar. Die richtige Anwendung des YAGNI-Prinzips hat nichts damit zu tun. Es ist ein Aspekt von gutem Design. Jedes der SOLID -Prinzipien ist auch ein Aspekt guten Designs. Sie können nicht jedes Prinzip in jeder Disziplin vollständig anwenden. Probleme in der realen Welt setzen Ihrem Code viele Kräfte auf, und einige von denen Push in entgegengesetzte Richtungen. Designprinzipien müssen all diese berücksichtigen, aber keine Handvoll Prinzipien kann in alle Situationen passen.

Schauen wir uns nun jedes Prinzip mit dem Verständnis an, dass sie zwar manchmal in verschiedene Richtungen ziehen, aber keineswegs in Konflikt stehen.

YAGNI wurde entwickelt, um Entwicklern dabei zu helfen, eine bestimmte Art von Nacharbeit zu vermeiden: die, die durch das Erstellen der falschen Sache entsteht. Dies geschieht, indem es uns anleitet, zu vermeiden, dass fehlerhafte Entscheidungen zu früh getroffen werden, basierend auf Annahmen oder Vorhersagen darüber, was sich unserer Meinung nach ändern oder in Zukunft benötigt wird. Die kollektive Erfahrung zeigt uns, dass wir uns normalerweise irren, wenn wir dies tun. Zum Beispiel würde YAGNI Ihnen sagen, dass Sie keine Schnittstelle erstellen sollen zum Zwecke der Wiederverwendbarkeit, es sei denn, Sie wissen im Moment, dass Sie mehrere Implementierer benötigen. In ähnlicher Weise würde YAGNI sagen, dass Sie keinen "ScreenManager" erstellen, um das einzelne Formular in einer Anwendung zu verwalten, es sei denn, Sie wissen im Moment, dass Sie mehr als einen Bildschirm haben werden.

Im Gegensatz zu dem, was viele Leute denken, ist SOLID nicht über Wiederverwendbarkeit, Generizität oder sogar Abstraktion. SOLID soll Ihnen helfen Schreiben Sie Code, der für Änderungen vorbereitet ist, ohne etwas darüber zu sagen, was diese spezifische Änderung sein könnte. Die fünf Prinzipien von SOLID erstellen eine Strategie zum Erstellen von Code, der ohne Änderungen flexibel ist übermäßig generisch und einfach zu sein, ohne naiv zu sein. Die richtige Anwendung von SOLID Code erzeugt kleine, fokussierte Klassen mit genau definierten Rollen und Grenzen. Das praktische Ergebnis ist, dass sich für alle erforderlichen Anforderungen ein Minimum ändert Die Anzahl der Klassen muss berührt werden. Ebenso wird bei jeder Codeänderung die Anzahl der "Ripple" auf andere Klassen minimiert.

Schauen wir uns die Beispielsituation an, die Sie haben, und sehen wir uns an, was YAGNI und SOLID) zu sagen haben. Sie erwägen eine gemeinsame Repository-Schnittstelle, da alle Repositorys von außen gleich aussehen. Der Wert einer gemeinsamen, generischen Schnittstelle ist jedoch die Fähigkeit, einen der Implementierer zu verwenden, ohne wissen zu müssen, um welchen es sich handelt. Es sei denn, in Ihrer App befindet sich irgendwo, wo dies notwendig oder nützlich wäre, sagt YAGNI, dass dies nicht der Fall ist es.

Es gibt 5 SOLID Prinzipien, die zu betrachten sind. S ist Einzelverantwortung. Dies sagt nichts über die Schnittstelle aus, aber es könnte etwas über Ihre konkreten Klassen aussagen. Es könnte argumentiert werden, dass die Behandlung des Datenzugriffs selbst erfolgt Am besten kann eine oder mehrere andere Klassen dafür verantwortlich gemacht werden, während die Verantwortung der Repositorys darin besteht, aus einem impliziten Kontext (CustomerRepository ist implizit ein Repository für Kundenentitäten) in explizite Aufrufe der allgemeinen Datenzugriffs-API zu übersetzen, wobei der Entitätstyp des Kunden angegeben wird.

O ist offen geschlossen. Hier geht es hauptsächlich um Vererbung. Dies gilt, wenn Sie versuchen, Ihre Repositorys von einer gemeinsamen Basis abzuleiten, die gemeinsame Funktionen implementiert, oder wenn Sie eine weitere Ableitung von den verschiedenen Repositorys erwarten. Aber du bist es nicht, also nicht.

L ist Liskov-Substituierbarkeit. Dies gilt, wenn Sie die Repositorys über die gemeinsame Repository-Schnittstelle verwenden möchten. Die Schnittstelle und die Implementierungen werden eingeschränkt, um die Konsistenz sicherzustellen und eine spezielle Behandlung für verschiedene Impelementer zu vermeiden. Der Grund dafür ist, dass eine solche spezielle Behandlung den Zweck einer Schnittstelle untergräbt. Es kann nützlich sein, dieses Prinzip zu berücksichtigen, da es Sie möglicherweise davor warnt, die gemeinsame Repository-Schnittstelle zu verwenden. Dies stimmt mit den Leitlinien von YAGNI überein.

Ich bin Schnittstellentrennung. Dies kann zutreffen, wenn Sie Ihren Repositorys verschiedene Abfragevorgänge hinzufügen. Die Schnittstellentrennung gilt, wenn Sie die Mitglieder einer Klasse in zwei Teilmengen aufteilen können, wobei eine von bestimmten Verbrauchern und die andere von anderen verwendet wird, aber wahrscheinlich keine Verbraucher beide Teilmengen verwenden. Die Anleitung besteht darin, zwei separate Schnittstellen anstelle einer gemeinsamen zu erstellen. In Ihrem Fall ist es unwahrscheinlich, dass das Abrufen und Speichern einzelner Instanzen von demselben Code verwendet wird, der allgemeine Abfragen ausführt. Daher kann es hilfreich sein, diese in zwei Schnittstellen zu unterteilen.

D ist Abhängigkeitsinjektion. Hier kommen wir zum gleichen Punkt wie beim S zurück. Wenn Sie Ihren Verbrauch der Datenzugriffs-API in ein separates Objekt aufgeteilt haben, besagt dieses Prinzip, dass Sie eine Instanz dieses Objekts nicht nur neu erstellen, sondern beim Erstellen übergeben sollten ein Repository. Dies erleichtert die Steuerung der Lebensdauer der Datenzugriffskomponente und eröffnet die Möglichkeit, Verweise darauf zwischen Ihren Repositorys auszutauschen, ohne sie zu einem Singleton machen zu müssen.

Es ist wichtig zu beachten, dass die meisten Prinzipien von SOLID] in dieser bestimmten Phase der App-Entwicklung nicht unbedingt gelten. Ob Sie beispielsweise den Datenzugriff unterbrechen sollten, hängt davon ab, wie kompliziert er ist. und ob Sie Ihre Repository-Logik testen möchten, ohne auf die Datenbank zuzugreifen. Dies klingt unwahrscheinlich (meiner Meinung nach leider), daher ist dies wahrscheinlich nicht erforderlich.

Nach all diesen Überlegungen stellen wir fest, dass YAGNI und SOLID tatsächlich einen gemeinsamen soliden, unmittelbar relevanten Ratschlag liefern: Es ist wahrscheinlich nicht erforderlich, eine gemeinsame generische Repository-Schnittstelle zu erstellen.

All diese sorgfältigen Überlegungen sind als Lernübung äußerst nützlich. Es ist zeitaufwändig, wenn Sie lernen, aber im Laufe der Zeit entwickeln Sie eine Intuition und werden sehr schnell. Sie wissen, was zu tun ist, müssen aber nicht alle diese Wörter denken, es sei denn, jemand bittet Sie, zu erklären, warum.

10
Chris Ammerman

Das Erweitern Ihres Codes mit Unit-Tests wird unter YAGNI niemals behandelt, da Sie ihn werden benötigen. Ich bin jedoch nicht davon überzeugt, dass Ihre Designänderungen an einer Schnittstelle mit einer einzelnen Implementierung die Testbarkeit des Codes tatsächlich erhöhen, da CustomerFactory bereits von einer Schnittstelle erbt und ohnehin jederzeit gegen eine MockCustomerFactory ausgetauscht werden kann.

10
DeadMG

Sie scheinen zu glauben, dass „gutes Design“ bedeutet, einer Art Idealogie und formalen Regeln zu folgen, die immer angewendet werden müssen, auch wenn sie nutzlos sind.

IMO das ist schlechtes Design. YAGNI ist ein Bestandteil guten Designs, niemals ein Widerspruch dazu.

6
Vector

Einige Leute argumentieren, dass Schnittstellennamen nicht mit I beginnen sollten. Ein besonderer Grund ist, dass Sie tatsächlich die Abhängigkeit davon verlieren, ob der angegebene Typ eine Klasse oder eine Schnittstelle ist.

Was hindert Sie daran, CustomerFactory zuerst eine Klasse zu sein und sie später in eine Schnittstelle umzuwandeln, die entweder von DefaultCustormerFactory oder UberMegaHappyCustomerPowerFactory3000 Implementiert wird? Das einzige, was Sie ändern müssen, ist der Ort, an dem die Implementierung instanziiert wird. Und wenn Sie ein weniger gutes Design haben, dann sind dies höchstens eine Handvoll Orte.

Refactoring ist ein Teil der Entwicklung. Besser wenig Code, der leicht umzugestalten ist, als eine Schnittstelle und eine Klasse für jede einzelne Klasse deklariert zu haben, sodass Sie jeden Methodennamen an mindestens zwei Stellen gleichzeitig ändern müssen.

Der eigentliche Punkt bei der Verwendung von Schnittstellen ist das Erreichen von Modularität, die möglicherweise die wichtigste Säule für gutes Design ist. Beachten Sie jedoch, dass ein Modul nicht nur durch seine Entkopplung von der Außenwelt definiert wird (obwohl wir es so von außen wahrnehmen), sondern auch durch seine innere Funktionsweise.
Ich möchte darauf hinweisen, dass die Entkopplung von Dingen, die von Natur aus zusammengehören, nicht viel Sinn macht. In gewisser Weise ist es so, als hätte man einen Schrank mit einem individuellen Regal pro Tasse.

Die Bedeutung liegt darin, ein großes, komplexes Problem in kleinere, einfachere Teilprobleme zu zerlegen. Und Sie müssen an dem Punkt anhalten, an dem sie ohne weitere Unterteilungen einfach genug werden, sonst werden sie tatsächlich komplizierter. Dies könnte als eine Folge von YAGNI gesehen werden. Und es bedeutet definitiv gutes Design.

Das Ziel ist nicht, ein lokales Problem mit einem einzigen Repository und einer einzigen Fabrik zu lösen. Das Ziel ist, dass diese Entscheidung keine Auswirkungen auf den Rest Ihrer Bewerbung hat. Darum geht es bei Modularität.
Sie möchten, dass Ihre Mitarbeiter Ihr Modul betrachten, eine Fassade mit einer Handvoll selbsterklärender Aufrufe sehen und sich sicher sind, dass sie diese verwenden können, ohne sich um all die potenziell hoch entwickelten Inneninstallationen kümmern zu müssen.

2
back2dos

In Ihrem Beispiel würde ich sagen, dass YAGNI sich durchsetzen sollte. Es kostet Sie nicht so viel, wenn Sie später Schnittstellen hinzufügen müssen. Ist es übrigens wirklich gut, eine Schnittstelle pro Klasse zu haben, wenn sie überhaupt keinem Ziel dient?

Noch ein Gedanke, vielleicht brauchen Sie manchmal nicht gutes Design, sondern ausreichendes Design. Hier ist eine sehr interessante Abfolge von Beiträgen zum Thema:

2
David

Können Sie jetzt ein vage vernünftiges ICustomerRepository aufschreiben? Zum Beispiel (wirklich hier angekommen, wahrscheinlich ein schlechtes Beispiel) haben Ihre Kunden in der Vergangenheit immer PayPal verwendet? Oder sind alle in der Firma begeistert davon, sich mit Alibaba zu treffen? Wenn ja, möchten Sie vielleicht jetzt das komplexere Design verwenden und Ihren Vorgesetzten weitsichtig erscheinen. :-)

Ansonsten warte. Das Erraten einer Schnittstelle, bevor Sie eine oder zwei tatsächliche Implementierungen haben, schlägt normalerweise fehl. Mit anderen Worten, verallgemeinern/abstrahieren/verwenden Sie kein ausgefallenes Entwurfsmuster, bis Sie einige Beispiele zum Verallgemeinern haben.

0
user949300

Sie erstellen ein interface, das in Zukunft mehrere Implementierungen vorwegnimmt. Sie können auch einen I<class> Für jede Klasse in Ihrer Codebasis haben. Tu es nicht.

Verwenden Sie einfach die einzelne Betonklasse nach YAGNI. Wenn Sie zu Testzwecken ein "Schein" -Objekt benötigen, verwandeln Sie die ursprüngliche Klasse in eine abstrakte Klasse mit zwei Implementierungen, eine mit der ursprünglichen konkreten Klasse und die andere mit der Scheinimplementierung.

Sie müssen natürlich alle Instanziierungen der ursprünglichen Klasse aktualisieren, um die neue konkrete Klasse zu instanziieren. Sie können dies umgehen, indem Sie einen statischen Konstruktor im Voraus verwenden.

YAGNI sagt, dass kein Code geschrieben werden soll, bevor er geschrieben werden muss.

Gutes Design sagt, Abstraktion zu verwenden.

Sie können beides haben. Eine Klasse ist eine Abstraktion.

0
Jesse

Warum die Marker-Schnittstellen? Es fällt mir auf, dass es nichts weiter tut als "taggen". Was ist der Sinn, wenn für jeden Werkstyp ein anderes "Tag" verwendet wird?

Der Zweck einer Schnittstelle besteht darin, einer Klasse das Verhalten "verhält sich wie" zu geben, um ihnen sozusagen "Repository-Fähigkeit" zu verleihen. Wenn sich also alle Ihre konkreten Repository-Typen wie dasselbe IRepository verhalten (sie implementieren alle IRepository), können sie alle von demselben Code auf dieselbe Weise behandelt werden - von genau demselben Code. Zu diesem Zeitpunkt ist Ihr Design erweiterbar. Hinzufügen konkreterer Repository-Typen, die alle als generische IRepository (s) behandelt werden - der gleiche Code behandelt alle konkreten Typen als "generische" Repositorys.

Schnittstellen dienen zum Umgang mit Dingen, die auf Gemeinsamkeiten basieren. Benutzerdefinierte Markierungsschnittstellen a) fügen jedoch kein Verhalten hinzu. und b) Sie zwingen, mit ihrer Einzigartigkeit umzugehen.

In dem Maße, in dem Sie nützliche Schnittstellen entwerfen, erhalten Sie die OO Güte, keinen speziellen Code schreiben zu müssen, um spezialisierte Klassen, Typen oder benutzerdefinierte Markierungsschnittstellen zu verarbeiten, die eine 1: 1-Korrelation zu konkreten Klassen aufweisen. Das ist sinnlose Redundanz.

Nebenbei kann ich eine Markierungsoberfläche sehen, wenn Sie zum Beispiel eine stark typisierte Sammlung vieler verschiedener Klassen benötigen. In der Sammlung sind sie alle "ImarkerInterface", aber wenn Sie sie herausziehen, müssen Sie sie in die richtigen Typen umwandeln.

0
radarbob