Ab wann 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 irgendwie ohne Reim oder Grund zusammen gehackt), aber nach dem Erstellen einer Reihe von Klassen (wir) mache keine TDD oder leider überhaupt keine Unit-Tests) Ich bin einen Schritt zurückgetreten und dachte, es würde gegen YAGNI verstoßen, weil ich ziemlich sicher weiß, dass wir nicht die Notwendigkeit haben, einige dieser Klassen zu erweitern.
Hier ist ein konkretes Beispiel für das, was ich meine: Ich habe eine Datenzugriffsebene, die eine Reihe gespeicherter Prozeduren umschließt, die ein rudimentäres Repository-ähnliches Muster 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 namens erstellt IRepository
. Ich habe dann jedoch eine "Marker" -Schnittstelle (dh 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 getan, um die Geschäftsobjekte aus DataReaders / DataSets zu erstellen, die von der gespeicherten Prozedur zurückgegeben wurden. Die Signatur meiner Repository-Klasse sieht normalerweise 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 Sorgen, dass ich gegen YAGNI verstoße, weil ich mit 99% iger Gewissheit weiß, dass es niemals einen Grund geben wird CustomerFactory
, diesem Endlager etwas anderes als ein konkretes zu geben. da wir keine Unit-Tests haben, brauche ich keine MockCustomerFactory
oder ähnliche Dinge, und so viele Schnittstellen könnten meine Mitarbeiter verwirren. Auf der anderen Seite scheint die konkrete Umsetzung der Fabrik ein Designgeruch zu sein.
Gibt es einen guten Weg, um einen Kompromiss zwischen korrektem Software-Design und einer Überarchitektur der Lösung zu finden? Ich frage mich, ob ich alle "einzelnen Implementierungsschnittstellen" haben muss oder ob ich ein bisschen gutes Design opfern und nur zum Beispiel die Basisschnittstelle und dann die einzelne konkrete haben kann und mich nicht um die Programmierung kümmern muss Schnittstelle, wenn die Implementierung jemals verwendet wird.
quelle
Antworten:
YAGNI.
Falsche Annahme.
Das ist kein "Opfer". Das ist gutes Design.
quelle
In den meisten Fällen führt die Vermeidung von Code, den Sie nicht benötigen, zu einem besseren Design. Das wartungsfreundlichste und zukunftssicherste Design verwendet die geringste Menge an gut benanntem, einfachem Code, der die Anforderungen erfüllt.
Die einfachsten Designs sind am einfachsten zu entwickeln. Nichts beeinträchtigt die Wartbarkeit so sehr wie nutzlose, überentwickelte Abstraktionsebenen.
quelle
YAGNI und SOLID (oder eine andere Entwurfsmethode) schließen sich nicht gegenseitig aus. Sie sind jedoch nahpolare Gegensätze. Sie müssen sich auch nicht zu 100% daran halten, aber es wird ein gewisses Geben und Nehmen geben. 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 FEST wird das Design. Das Gegenteil kann auch der Fall sein; Viele Male in der Entwicklung wird ein Design SOLID "on faith" implementiert. Sie sehen nicht, wie Sie es brauchen, aber Sie haben nur eine Ahnung. Dies könnte der Fall sein (und es wird immer wahrer, je mehr Erfahrung Sie sammeln), aber es könnte Sie auch in so viel technische Schulden stecken wie ein "Do-it-Light" -Ansatz mit einem Schlag. Anstelle einer DIL-Codebasis mit "Spaghetti-Code" erhalten Sie möglicherweise "Lasagne-Code". Das Hinzufügen einer Methode oder eines neuen Datenfelds auf so vielen Ebenen ist ein tagelanger Prozess, bei dem mit nur einer Implementierung Service-Proxys und lose gekoppelte Abhängigkeiten durchlaufen werden. Oder Sie könnten am Ende mit "Ravioli-Code" enden, der in so kleinen, mundgerechten Stücken vorliegt, dass Sie 50 Methoden mit jeweils 3 Zeilen durchlaufen, wenn Sie sich in der Architektur nach oben, unten, links oder rechts bewegen.
Ich habe es in anderen Antworten gesagt, aber hier ist es: Lass es beim ersten Durchgang funktionieren. Machen Sie es im zweiten Durchgang elegant. Machen Sie es beim dritten Durchgang SOLID.
Aufschlüsselung:
Wenn Sie zum ersten Mal eine Codezeile schreiben, muss dies einfach funktionieren. Soweit Sie wissen, handelt es sich an diesem Punkt um eine einmalige Angelegenheit. 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 gehen Sie davon aus, dass Sie es nie wieder sehen werden.
Wenn Sie das nächste Mal mit dem Cursor in diese Codezeile springen, haben Sie Ihre Hypothese von dem Zeitpunkt an widerlegt, als Sie sie zum ersten Mal geschrieben haben. Sie überarbeiten diesen Code, wahrscheinlich, um ihn zu erweitern oder an anderer Stelle zu verwenden, sodass es sich nicht um einen einmaligen Code handelt. Nun sollten einige Grundprinzipien wie DRY (nicht wiederholen) 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, fügen Sie möglicherweise einige Kommentare hinzu, aber insgesamt sollte sich Ihr Code selbst dokumentieren. Jetzt ist Ihr Code gut organisiert, wenn auch noch eng miteinander verbunden, und jeder, der ihn ansieht, kann leicht herausfinden, was Sie tun, indem Sie den Code lesen, anstatt ihn Zeile für Zeile zu verfolgen.
Wenn der Cursor zum dritten Mal diesen Code eingibt, ist das wahrscheinlich eine große Sache. Sie erweitern es entweder noch einmal oder es ist an mindestens drei anderen Stellen in der Codebasis nützlich geworden. An diesem Punkt ist es ein Schlüssel-, wenn nicht Kernelement Ihres Systems und sollte als solches strukturiert werden. An diesem Punkt haben Sie in der Regel auch das Wissen darüber, wie es bisher verwendet wurde, sodass Sie gute Entwurfsentscheidungen treffen können, wie der Entwurf entworfen werden soll, um diese und etwaige neue Verwendungen zu rationalisieren. Nun sollten die SOLID-Regeln in die Gleichung eingehen. Extrahieren Sie Klassen mit Code für bestimmte Zwecke, definieren Sie gemeinsame Schnittstellen für Klassen mit ähnlichen Zwecken oder Funktionen, richten Sie lose gekoppelte Abhängigkeiten zwischen Klassen ein und entwerfen Sie die Abhängigkeiten so, dass Sie sie einfach hinzufügen, entfernen oder austauschen können.
Wenn Sie diesen Code von nun an erweitern, erneut implementieren oder erneut verwenden müssen, ist er in dem "Black Box" -Format verpackt und abstrahiert, das wir alle kennen und lieben. Schließen Sie es an, wo immer Sie es benötigen, oder fügen Sie eine neue Variation des Themas als neue Implementierung der Schnittstelle hinzu, ohne die Verwendung dieser Schnittstelle ändern zu müssen.
quelle
Stattdessen bevorzuge ich WTSTWCDTUAWCROT?
(Was ist das Einfachste, was wir tun können, was nützlich ist und was wir am Donnerstag veröffentlichen können?)
Einfachere Akronyme stehen auf meiner Liste, haben aber keine Priorität.
quelle
YAGNI und gutes Design sind kein Widerspruch. Bei YAGNI geht es darum, zukünftige Bedürfnisse (nicht) zu unterstützen. Gutes Design geht es darum, transparent , was eine Software macht jetzt und wie sie tut dies.
Wird die Einführung einer Factory Ihren bestehenden Code vereinfachen? Wenn nicht, fügen Sie es nicht hinzu. Wenn dies z. B. beim Hinzufügen von Tests der Fall ist (was Sie tun sollten!), Fügen Sie dies hinzu.
Bei YAGNI geht es nicht darum , die Komplexität zu erhöhen, um zukünftige Funktionen zu unterstützen.
Bei gutem Design geht es darum , Komplexität zu beseitigen und gleichzeitig alle aktuellen Funktionen zu unterstützen.
quelle
Sie sind nicht in Konflikt, Ihre Ziele sind falsch.
Was versuchst du zu erreichen?
Sie möchten qualitativ 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.
(Interessierte, das nennt man verdunstende Wolken )
Also, was treibt das an?
Welche davon können wir lösen? Nun, es sieht so aus, als würde man keine Zeit verschwenden wollen und Code aufzublähen ist ein großartiges Ziel und macht Sinn. Was ist mit dem ersten? Können wir herausfinden, was wir zum Codieren benötigen?
Lassen Sie uns all das neu formulieren
Sie brauchen keinen Kompromiss, Sie brauchen jemanden, der das Team leitet, kompetent ist und eine Vision für das gesamte Projekt hat. Sie brauchen jemanden, der planen kann, was Sie brauchen, anstatt jeden von Ihnen in Dinge zu werfen, die Sie NICHT brauchen, weil Sie sich der Zukunft so unsicher sind, weil ... warum? Ich sage dir warum, es liegt daran, dass niemand unter euch allen einen verdammten Plan hat. Sie versuchen, Codestandards einzuführen, um ein völlig separates Problem zu beheben. Ihr primäres Problem, das Sie lösen müssen, ist eine klare Roadmap und ein klares Projekt. Wenn Sie das haben, können Sie sagen: "Codestandards helfen uns, dieses Ziel als Team effektiver zu erreichen." Dies ist die absolute Wahrheit, liegt jedoch außerhalb des Rahmens dieser Frage.
Besorgen Sie sich einen Projekt- / Teammanager, der diese Aufgaben ausführen kann. Wenn Sie eine haben, müssen Sie sie nach einer Karte fragen und das YAGNI-Problem erklären, das auftritt, wenn keine Karte vorhanden ist. Wenn sie grob inkompetent sind, schreiben Sie den Plan selbst und sagen Sie: "Hier ist mein Bericht über die Dinge, die wir brauchen. Bitte überprüfen Sie ihn und teilen Sie uns Ihre Entscheidung mit."
quelle
Das Ermöglichen, dass Ihr Code durch Komponententests erweitert wird, wird unter YAGNI niemals abgedeckt, da Sie es benötigen werden. Ich bin jedoch nicht davon überzeugt, dass Ihre Designänderungen an einer Schnittstelle mit nur einer 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.
quelle
Die Frage ist ein falsches Dilemma. Die ordnungsgemäße Anwendung des YAGNI-Prinzips ist keine unabhängige Sache. Das ist ein Aspekt von gutem Design. Jedes der SOLID-Prinzipien ist auch ein Aspekt guten Designs. Sie können nicht immer jedes Prinzip in jeder Disziplin vollständig anwenden. Probleme in der realen Welt bringen eine Menge Kräfte in Ihren Code, und einige davon gehen in entgegengesetzte Richtungen. Prinzipien des Designs müssen all das 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 inhärent miteinander in Konflikt stehen.
YAGNI wurde entwickelt, um Entwicklern dabei zu helfen, eine bestimmte Art von Nacharbeit zu vermeiden: die, die aus dem Bauen der falschen Sache resultiert. Dies geschieht, indem es uns anleitet, Fehlentscheidungen zu vermeiden, die auf Annahmen oder Vorhersagen darüber beruhen, was sich unserer Meinung nach in Zukunft ändern oder erforderlich sein wird. Die kollektive Erfahrung zeigt uns, dass wir uns in der Regel irren, wenn wir dies tun. Zum Beispiel würde YAGNI Sie auffordern, keine Schnittstelle zum Zwecke der Wiederverwendbarkeit zu erstellen , es sei denn, Sie wissen gerade, dass Sie mehrere Implementierer benötigen. Ebenso würde YAGNI sagen, dass Sie keinen "ScreenManager" zum Verwalten eines einzelnen Formulars in einer Anwendung erstellen sollten, es sei denn, Sie wissen gerade, dass Sie mehr als einen Bildschirm haben werden.
Im Gegensatz zu dem, was viele Leute denken, geht es bei SOLID nicht um Wiederverwendbarkeit, Generizität oder gar Abstraktion. SOLID soll Ihnen dabei helfen, Code zu schreiben, der für Änderungen vorbereitet ist , ohne etwas darüber zu sagen, was für eine bestimmte Änderung dies sein könnte. Die fünf Prinzipien von SOLID schaffen eine Strategie zum Erstellen von Code, die flexibel ist, ohne zu generisch zu sein, und einfach, ohne naiv zu sein. Die ordnungsgemäße Anwendung von SOLID-Code erzeugt kleine, fokussierte Klassen mit genau definierten Rollen und Grenzen. Das praktische Ergebnis ist, dass eine Mindestanzahl von Klassen berührt werden muss, damit sich die Anforderungen ändern können. Und in ähnlicher Weise gibt es für jede Codeänderung eine minimierte Menge an "Ripple" bis zu anderen Klassen.
Schauen wir uns die Beispielsituation an, die Sie haben, und lassen Sie uns sehen, was YAGNI und SOLID zu sagen haben. Sie ziehen eine gemeinsame Repository-Schnittstelle in Betracht, 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 welche es sich handelt. Sofern es in Ihrer App keine Stelle gibt, an der dies notwendig oder nützlich wäre, sagt YAGNI, dass Sie dies nicht tun sollten.
Es gibt 5 SOLID-Prinzipien zu betrachten. 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 Verarbeitung des Datenzugriffs selbst am besten in die Verantwortung einer oder mehrerer anderer Klassen fällt, während die Verantwortung der Repositorys darin besteht, aus einem impliziten Kontext (CustomerRepository ist ein Repository, das implizit für Entitäten des Kunden bestimmt ist) explizite Aufrufe an das zu übertragen Allgemeine Datenzugriffs-API, die den Entitätstyp des Kunden angibt.
O ist offen-geschlossen. Hier geht es hauptsächlich um Vererbung. Dies würde zutreffen, wenn Sie versuchen, Ihre Repositorys von einer gemeinsamen Basis abzuleiten, die gemeinsame Funktionen implementiert, oder wenn Sie erwarten, dass Sie weitere Informationen von den verschiedenen Repositorys ableiten. Aber das bist du nicht, also nicht.
L ist Liskov Substituierbarkeit. Dies gilt, wenn Sie die Repositorys über die gemeinsame Repository-Schnittstelle verwenden möchten. Es enthält Einschränkungen für die Schnittstelle und die Implementierungen, um die Konsistenz zu gewährleisten 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 in Betracht zu ziehen, 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 beginnen, Ihren Repositorys verschiedene Abfragevorgänge hinzuzufügen. Die Schnittstellentrennung wird angewendet, wenn Sie die Mitglieder einer Klasse in zwei Teilmengen aufteilen können, wobei eine von bestimmten Verbrauchern und die andere von anderen Verbrauchern verwendet wird, aber wahrscheinlich kein Verbraucher beide Teilmengen verwendet. Die Anleitung besteht darin, statt einer gemeinsamen Schnittstelle zwei separate Schnittstellen zu erstellen. In Ihrem Fall ist es unwahrscheinlich, dass das Abrufen und Speichern einzelner Instanzen von demselben Code verbraucht wird, der für allgemeine Abfragen verwendet wird. Daher ist es möglicherweise hilfreich, diese in zwei Schnittstellen zu unterteilen.
D ist Abhängigkeitsinjektion. Hier kommen wir zum selben Punkt wie das S. Wenn Sie Ihren Verbrauch der Datenzugriffs-API in ein separates Objekt aufgeteilt haben, besagt dieses Prinzip, dass Sie es beim Erstellen übergeben sollten, anstatt nur eine Instanz dieses Objekts neu zu erstellen ein Repository. Auf diese Weise können Sie die Lebensdauer der Datenzugriffskomponente einfacher steuern und Verweise auf diese Komponente zwischen Ihren Repositorys austauschen, ohne sie zu einem Singleton machen zu müssen.
Es ist wichtig zu beachten, dass die meisten SOLID-Prinzipien nicht unbedingt in diesem bestimmten Stadium der Entwicklung Ihrer App 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. Es hört sich so an, als wäre dies unwahrscheinlich (meiner Meinung nach leider), also ist es wahrscheinlich nicht notwendig.
Nach all diesen Überlegungen stellen wir fest, dass YAGNI und SOLID tatsächlich einen gemeinsamen soliden, sofort relevanten Ratschlag geben: Es ist wahrscheinlich nicht erforderlich, eine gemeinsame generische Repository-Schnittstelle zu erstellen.
All diese sorgfältigen Überlegungen sind äußerst nützlich als Lernübung. Es ist zeitaufwändig, wie Sie lernen, aber im Laufe der Zeit entwickeln Sie Intuition und werden sehr schnell. Sie wissen, was zu tun ist, müssen aber nicht über all diese Wörter nachdenken, es sei denn, jemand fragt Sie, warum.
quelle
Sie scheinen zu glauben, dass „gutes Design“ bedeutet, eine Art Idealogie und formale Regeln zu befolgen, die immer angewendet werden müssen, auch wenn sie unbrauchbar sind.
IMO das ist schlechtes Design. YAGNI ist ein Bestandteil guten Designs, niemals ein Widerspruch dazu.
quelle
In Ihrem Beispiel würde ich sagen, dass sich YAGNI durchsetzen sollte. Es kostet Sie nicht so viel, wenn Sie später Schnittstellen hinzufügen müssen. Übrigens, ist es wirklich gut, ein Interface pro Klasse zu haben, wenn es überhaupt keinem Ziel dient?
Ein weiterer Gedanke, vielleicht ist das, was Sie manchmal brauchen, nicht gutes Design, sondern ausreichendes Design. Hier ist eine sehr interessante Abfolge von Beiträgen zum Thema:
quelle
Einige Leute argumentieren, dass Schnittstellennamen nicht mit I beginnen sollten. Insbesondere liegt ein Grund darin, 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 vonDefaultCustormerFactory
oder implementiert wirdUberMegaHappyCustomerPowerFactory3000
? 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 ist 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 deklarieren zu lassen, sodass Sie jeden Methodennamen an mindestens zwei Stellen gleichzeitig ändern müssen.
Der eigentliche Punkt bei der Verwendung von Schnittstellen ist die 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 ist (auch wenn wir es so aus der Außenperspektive betrachten), sondern auch durch seine innere Arbeitsweise.
Ich möchte darauf hinweisen, dass das Entkoppeln von Dingen, die von Natur aus zusammengehören, wenig 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 Unterteilung 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 irgendwie zu lösen. Das Ziel ist, dass diese Entscheidung keinen Einfluss auf den Rest Ihrer Bewerbung hat. Darum geht es bei Modularität.
Sie möchten, dass Ihre Mitarbeiter auf Ihr Modul schauen, eine Fassade mit einer Handvoll selbsterklärender Anrufe sehen und sich sicher sind, dass sie sie verwenden können, ohne sich um alle potenziell raffinierten Inneninstallationen kümmern zu müssen.
quelle
Sie erstellen eine
interface
Vielzahl von Implementierungen für die Zukunft. Sie könnten genauso gut eineI<class>
für jede Klasse in Ihrer Codebasis haben. Nicht.Verwenden Sie einfach die Einzelbetonklasse nach YAGNI. Wenn Sie zu Testzwecken ein "Mock" -Objekt benötigen, wandeln Sie die ursprüngliche Klasse in eine abstrakte Klasse mit zwei Implementierungen um, eine mit der ursprünglichen konkreten Klasse und die andere mit der Mock-Implementierung.
Sie müssen natürlich alle Instanziierungen der ursprünglichen Klasse aktualisieren, um die neue konkrete Klasse zu instanziieren. Sie können das umgehen, indem Sie einen statischen Konstruktor verwenden.
YAGNI sagt, dass Code nicht geschrieben werden soll, bevor er geschrieben werden muss.
Gutes Design sagt Abstraktion.
Sie können beides haben. Eine Klasse ist eine Abstraktion.
quelle
Warum die Markerschnittstellen? Es fällt mir auf, dass es nichts weiter tut als "Markieren". Wozu dient ein anderes "Tag" für jeden Fabriktyp?
Der Zweck einer Schnittstelle besteht darin, einer Klasse ein Verhalten zu geben, das dem Verhalten einer Klasse ähnlich ist, und ihnen sozusagen Repository-Fähigkeit zu verleihen. Wenn sich also alle Ihre konkreten Repository-Typen wie das gleiche IRepository verhalten (alle implementieren IRepository), können sie alle von anderem Code auf die gleiche Weise behandelt werden - von genau demselben Code. An diesem Punkt ist Ihr Design erweiterbar. Hinzufügen konkreterer Repository-Typen, die alle als generische IRepositorys behandelt werden - derselbe Code behandelt alle konkreten Typen als "generische" Repositorys.
Schnittstellen dienen zur Behandlung von Dingen, die auf Gemeinsamkeiten basieren. Benutzerdefinierte Markierungsschnittstellen a) fügen jedoch kein Verhalten hinzu. und b) zwinge dich, mit ihrer Einzigartigkeit umzugehen.
In dem Maße, in dem Sie nützliche Schnittstellen entwerfen, haben Sie die OO-Güte, keinen speziellen Code schreiben zu müssen, um spezielle Klassen, Typen oder benutzerdefinierte Markierungsschnittstellen zu verarbeiten, die eine 1: 1-Korrelation zu konkreten Klassen aufweisen. Das ist sinnlose Redundanz.
Off Hand kann ich eine Markierungsoberfläche sehen, wenn Sie beispielsweise eine stark typisierte Sammlung von vielen verschiedenen Klassen benötigen. In der Sammlung sind sie alle "ImarkerInterface", aber wenn Sie sie herausziehen, müssen Sie sie in die richtigen Typen umwandeln.
quelle
Können Sie jetzt ein vage vernünftiges ICustomerRepository aufschreiben? ZB haben Ihre Kunden in der Vergangenheit immer PayPal verwendet (was hier wirklich ein schlechtes Beispiel ist)? Oder jeder in der Firma ist begeistert von Alibaba? In diesem Fall möchten Sie möglicherweise jetzt das komplexere Design verwenden und Ihren Vorgesetzten gegenüber 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 ein ausgefallenes Entwurfsmuster erst, wenn Sie ein paar Beispiele zur Verallgemeinerung haben.
quelle