Sind zirkuläre classnabhängigkeiten aus Sicht des Codierungsstils schlecht?

Sind zirkuläre classnabhängigkeiten aus Sicht des Codierungsstils schlecht?

Beispiel:

In einer databaseanwendung haben wir zwei classn, eine kapselt Informationen über eine einzelne database ( DBInfo ) und eine class, die eine databaseverbindung erstellen kann. ( ConnFactory )

DBInfo verfügt über eine getConnection Methode, die ConnFactory , um eine Verbindung zu erstellen. Aber ConnFactory selbst benötigt dazu ein DBInfo Objekt.

So: (Alle Codierungsstile werden aus Gründen der Lesbarkeit ignoriert)

 class DBInfo { String name; String connectionUrl; Connection getConnection() { return ConnFactory.getConnection(this); } } class ConnFactory { Connection getConnection(DBInfo toWhat) { return new Connection(toWhat.connectionUrl); } } 

Meine Kollegen argumentieren, dass dies eine schlechte Praxis sei und es wäre besser, wenn es nur eine Richtung von Abhängigkeiten gäbe und keine kreisförmigen wie hier.

Ist das eine schlechte Praxis, ein Anti-Pattern oder ein Code-Geruch? Gibt es irgendwelche Nachteile?

Im Allgemeinen würde ich kreisförmige Abhängigkeiten einen Code-Geruch nennen. Beachten Sie, dass der Begriff “Code Smell” hauptsächlich darauf hinweist, dass es sich hier um einen Code handelt, der besondere Aufmerksamkeit erfordert und wahrscheinlich von einem Redesign profitieren wird.

In den meisten Fällen würde ich stark auf ein Design achten, bei dem eine zirkuläre Abhängigkeit nicht notwendig ist, aber in seltenen Fällen kann es in Ordnung sein.

In Ihrem Beispiel scheint ConnFactory redundant zu sein, aber das könnte daran liegen, dass Ihr Beispiel getrimmt wurde. Es scheint mir jedoch, dass die Verbindungs-Erstellungslogik besser wäre, wenn sie in die DBInfo-class verschoben würde. Wenn Sie bereits eine class haben, die Daten über eine database enthält, scheint es nur natürlich zu sein, dass sie dafür verantwortlich ist, eine Verbindung zu dieser database herzustellen.

Ja, im Allgemeinen sind zirkuläre Abhängigkeiten schlecht, aber nicht immer böse. Probleme mit zirkulären Abhängigkeiten umfassen enge Kopplung, gegenseitig abhängige Module und allgemein Dominoeffekt, wenn Änderungen in einem Modul zu anderen Modulen übertragen werden.

Das heißt, Ihr Code verletzt das Prinzip der einfachen Verantwortlichkeit, indem DBInfo nicht nur Informationen über die database speichert, sondern auch dafür verantwortlich ist, Connection Objekte zu erhalten. Entfernen Sie diese bestimmte functionalität in eine separate class und alles wird gut.

Nicht unbedingt

Ich glaube nicht, dass zirkuläre Abhängigkeiten auf der classngranularitätsstufe schlecht sind. Ich sehe kein Problem, wenn zwei, drei oder vielleicht vier classn voneinander abhängig sind. (Ich sage nicht, dass dies etwas ist, was Sie wollen, aber es kann unter bestimmten Umständen in Ordnung sein).

Es ist ein Problem, wenn Sie auf Paket- oder Modulebene eine gegenseitige Abhängigkeit haben, und zwar aus allen oben genannten Gründen.

Dieser Code funktioniert nur, wenn ConnFactory.getConnection() statisch ist. Eine bessere Lösung wäre, getConnection() eine Instanzmethode von ConnFactory . Dann kann Ihre DBInfo eine ConnFactory als Argument nehmen (möglicherweise in einem Konstruktor, wenn Sie einen überladenen Konstruktor haben). Ich denke jedoch, dass die Verwendung der statischen Methode für diesen Fall eher eine schlechte Übung ist als die zirkuläre Referenz.

Wenn Sie diese Route jedoch gehen würden, würde ich auch eine Schnittstelle IConnFactory , mit der DBInfo interagieren würde und die ConnFactory implementieren würde. Dann gibt es keine zirkuläre Referenz – sowohl DBInfo als auch ConnFactory würden von IConnFactory abhängen, was von keinem der beiden DBInfo .

Ich weiß nur, dass zirkuläre Abhängigkeiten zu einem Problem werden können, wenn Sie ein Dependency Injection Framework wie Structure Map verwenden. Die meisten dieser Frameworks haben Probleme beim Umgang mit zirkulären Abhängigkeiten, was manchmal zu einer Stapelüberlauf-Ausnahme führt (verzeihe das Wortspiel :-)). Deshalb tendiere ich dazu, mich zu verstecken, es sei denn, absolut notwendig und nicht zu vermeiden.

Was ist mit einer bidirektionalen Eins-zu-Viele-Beziehung, die in jeder Anwendung, die eine ORM-Schicht verwendet, so häufig vorkommt? Ist das nicht eine zirkuläre Abhängigkeit?

Ist es schlecht / Code-Geruch?

Kreisförmige Abhängigkeiten sind schlecht, weil:

  • zwei Abhängigkeiten sind mehr als eins
  • Sie können nicht inkrementell testen (ohne eine davon zu verspotten, was für kleine, eng verbundene Dinge albern wäre).

Sie können all das mit Schnittstellen tun, um die zirkuläre Abhängigkeit bei Bedarf zu unterbrechen, aber die einfache Minimallösung besteht darin, DBInfo zu einer verschachtelten class von ConnFactory zu machen. Eine Einheit, die sich selbst bezeichnet, ist nicht kreisförmig.