Ich verwende eine große Schnittstelle mit ungefähr 50 Methoden, um auf eine Datenbank zuzugreifen. Die Schnittstelle wurde von einem Kollegen von mir geschrieben. Wir haben das besprochen:
Ich: 50 Methoden sind zu viel. Es ist ein Code-Geruch.
Kollege: Was soll ich dagegen tun? Sie möchten den DB-Zugriff - Sie haben ihn.
Ich: Ja, aber es ist unklar und in Zukunft kaum zu warten.
Kollege: OK, Sie haben Recht, es ist nicht schön. Wie soll die Schnittstelle dann aussehen?
Ich: Wie wäre es mit 5 Methoden, die Objekte zurückgeben, die jeweils 10 Methoden haben?
Mmmh, aber wäre das nicht dasselbe? Führt dies wirklich zu mehr Klarheit? Lohnt sich die Mühe?
Hin und wieder bin ich in einer Situation, in der ich eine Schnittstelle möchte und das erste, was mir in den Sinn kommt, ist eine große Schnittstelle. Gibt es dafür ein allgemeines Entwurfsmuster?
Update (als Antwort auf SJuans Kommentar):
Die "Art von Methoden": Es ist eine Schnittstelle zum Abrufen von Daten aus einer Datenbank. Alle Methoden haben die Form (Pseudocode)
List<Typename> createTablenameList()
Methoden und Tabellen stehen nicht genau in einer 1-1-Beziehung, der Schwerpunkt liegt eher auf der Tatsache, dass Sie immer eine Art Liste erhalten, die aus einer Datenbank stammt.
quelle
UserDao
und aCustomerDao
und aProductDao
)List<WeatherDataRecord> createWeatherDataTable() {db.open(); return db.select("*", "tbl_weatherData");}
Antworten:
Ja, 50 Methoden sind ein Codegeruch, aber ein Codegeruch bedeutet, einen zweiten Blick darauf zu werfen, nicht dass er automatisch falsch ist. Wenn jeder Client, der diese Klasse verwendet, möglicherweise alle 50 Methoden benötigt, gibt es möglicherweise keinen Fall, um sie aufzuteilen. Dies ist jedoch unwahrscheinlich. Mein Punkt ist, dass das willkürliche Aufteilen einer Schnittstelle schlimmer sein kann, als sie überhaupt nicht aufzuteilen.
Es gibt kein einziges Muster, um das Problem zu beheben, aber das Prinzip, das den gewünschten Status beschreibt, ist das Prinzip der Schnittstellentrennung (das 'I' in SOLID), das besagt, dass kein Client gezwungen werden sollte, sich auf Methoden zu verlassen, die er nicht verwendet .
Die ISP-Beschreibung gibt Ihnen einen Hinweis, wie Sie das Problem beheben können: Sehen Sie sich den Client an . Wenn man sich nur eine Klasse ansieht, scheint es oft so, als ob alles zusammen gehört, aber klare Trennlinien entstehen, wenn man sich die Kunden ansieht , die diese Klasse verwenden. Berücksichtigen Sie beim Entwerfen einer Schnittstelle immer zuerst die Clients.
Die andere Möglichkeit zu bestimmen, ob und wo eine Schnittstelle aufgeteilt werden soll, besteht in einer zweiten Implementierung. Was häufig passiert, ist, dass Ihre zweite Implementierung nicht viele Methoden benötigt, daher sollten diese eindeutig in ihre eigene Schnittstelle aufgeteilt werden.
quelle
Das sind keine guten Kriterien (in dieser Aussage gibt es eigentlich überhaupt keine Kriterien). Sie können sie gruppieren nach (vorausgesetzt, Ihre Anwendung ist für meine Beispiele eine Finanztransaktions-App):
Wenn Sie die richtigen Kriterien wählen, auf jeden Fall. Wenn nicht, definitiv nicht :).
Einige Beispiele:
In ADODB-Objekten finden Sie ein vereinfachtes Beispiel für OO- Grundelemente (Ihre DB-API bietet dies wahrscheinlich bereits an.)
Schauen Sie sich das Django-Datenmodell ( https://docs.djangoproject.com/de/dev/topics/db/models/ ) für eine Datenmodellidee mit einem hohen Abstraktionsgrad an (in C ++ benötigen Sie wahrscheinlich etwas mehr Kesselplatte Code, aber es ist eine schöne Idee). Diese Implementierung wurde unter Berücksichtigung einer "Modell" -Rolle innerhalb des MVC-Entwurfsmusters entworfen.
In der SQLite-API finden Sie eine flache API-Idee ( http://www.sqlite.org/c3ref/funclist.html ), die nur aus funktionalen Grundelementen (C-API) besteht.
quelle
Es ist ein Design-Anti-Muster, das als monolithische Klasse bezeichnet wird . 50 Methoden in einer Klasse oder Schnittstelle zu haben, ist eine wahrscheinliche Verletzung des SRP . Die monolithische Klasse entsteht, weil sie versucht, für alle alles zu sein.
DCI- Adressen Methode aufblähen. Im Wesentlichen könnten die vielen Verantwortlichkeiten einer Klasse auf Rollen aufgeteilt (auf andere Klassen verlagert) werden, die nur in bestimmten Kontexten relevant sind. Die Anwendung von Rollen kann auf verschiedene Arten erreicht werden, einschließlich Mixins oder Dekorateure . Dieser Ansatz hält den Unterricht fokussiert und schlank.
Dies schlägt vor, alle Rollen zu instanziieren, wenn das Objekt selbst instanziiert wird. Aber warum sollten Sie Rollen instanziieren, die Sie möglicherweise nicht benötigen? Instanziieren Sie stattdessen eine Rolle in dem Kontext, in dem Sie sie tatsächlich benötigen.
Wenn Sie feststellen, dass eine Umgestaltung in Richtung DCI nicht offensichtlich ist, können Sie ein einfacheres Besuchermuster wählen . Es bietet einen ähnlichen Vorteil, ohne die Erstellung von Anwendungsfallkontexten zu betonen.
EDIT: Mein Denken darüber hat einige geändert. Ich gab eine alternative Antwort.
quelle
Es scheint mir, dass jede andere Antwort den Punkt verfehlt. Der Punkt ist, dass eine Schnittstelle idealerweise einen atomaren Verhaltensblock definieren sollte. Das ist das Ich in SOLID.
Eine Klasse sollte eine Verantwortung haben, dies kann jedoch mehrere Verhaltensweisen umfassen. Um bei einem typischen Datenbank-Client-Objekt zu bleiben, bietet dies möglicherweise die volle CRUD-Funktionalität. Das wären vier Verhaltensweisen: Erstellen, Lesen, Aktualisieren und Löschen. In einer reinen SOLID-Welt würde der Datenbankclient nicht IDatabaseClient implementieren, sondern ICreator, IReader, IUpdater und IDeleter entfernen.
Dies hätte eine Reihe von Vorteilen. Wenn man nur die Klassendeklaration liest, lernt man sofort viel über die Klasse. Die von ihr implementierten Schnittstellen erzählen die ganze Geschichte. Zweitens, wenn das Client-Objekt als Argument übergeben werden soll, hat man jetzt verschiedene nützliche Optionen. Es könnte als IReader übergeben werden und man könnte sicher sein, dass der Angerufene nur lesen kann. Verschiedene Verhaltensweisen können separat getestet werden.
Beim Testen ist es jedoch üblich, eine Schnittstelle einfach auf eine Klasse zu legen, die eine 1: 1-Replik der vollständigen Klassenschnittstelle ist. Wenn Sie sich nur um das Testen kümmern, kann dies ein gültiger Ansatz sein. Es ermöglicht Ihnen, Dummies ziemlich schnell zu machen. Aber es ist kaum jemals FEST und wirklich ein Missbrauch von Schnittstellen für einen bestimmten Zweck.
Also ja, 50 Methoden sind ein Geruch, aber es hängt von der Absicht und dem Zweck ab, ob es schlecht ist oder nicht. Es ist sicherlich nicht ideal.
quelle
In den Datenzugriffsschichten sind in der Regel viele Methoden an eine Klasse gebunden. Wenn Sie jemals mit Entity Framework oder anderen ORM-Tools gearbeitet haben, werden Sie feststellen, dass sie Hunderte von Methoden generieren. Ich gehe davon aus, dass Sie und Ihr Kollege es manuell implementieren. Es ist nicht notwendig, einen Code zu riechen, aber es ist nicht schön anzusehen. Ohne Ihre Domain zu kennen, ist es schwer zu sagen.
quelle
Ich verwende Protokolle (nenne sie Schnittstellen, wenn du willst) fast universell für alle APIs mit FP und OOP. (Erinnern Sie sich an die Matrix? Es gibt keine Konkretionen!) Es gibt natürlich konkrete Typen, aber im Rahmen eines Programms wird jeder Typ als etwas angesehen, das in einem bestimmten Kontext eine Rolle spielt.
Dies bedeutet, dass Objekte, die in Programmen, in Funktionen usw. übergeben werden, als abstrakte Entitäten mit benannten Verhaltenssätzen betrachtet werden können. Man kann sich vorstellen, dass das Objekt eine Rolle spielt, bei der es sich um eine Reihe von Protokollen handelt. Eine Person (konkreter Typ) könnte ein Mann, ein Vater, ein Ehemann, ein Freund und ein Angestellter sein, aber ich kann mir nicht viele Funktionen vorstellen, die die Entität als die Summe von mehr als zwei davon betrachten würden.
Ich denke, es ist möglich, dass ein komplexes Objekt eine Reihe verschiedener Protokolle aushält, aber es würde Ihnen immer noch schwer fallen, zu einer API mit 50 Methoden zu gelangen. Die meisten Protokolle haben 1 oder 2 Methoden, vielleicht 3, aber niemals 50! Jede Entität mit 50 Methoden sollte aus einer Reihe kleinerer Komponenten mit jeweils eigenen Verantwortlichkeiten bestehen. Die gesamte Entität würde eine einfachere Schnittstelle darstellen, die die Gesamtsumme der darin enthaltenen Apis abstrahiert.
Anstatt in Objekten und Methoden zu denken, sollten Sie in Abstraktionen und Verträgen denken und welche Rolle ein Subjekt in einem bestimmten Kontext spielt.
quelle