2009-07-16 17 views
11

Ich lese gerade Agile Prinzipien, Muster und Praktiken in C# von R. Martin und M. Martin und sie schlagen in ihrem Buch vor, alle Ihre Schnittstellen in einem separaten Projekt, z. Schnittstellen.Organisieren von Interfaces

Als Beispiel, wenn ich ein Gui Projekt haben, dass alle meine Klassen benutzerdefinierte Gui enthält, werde ich ihre Schnittstellen in den Schnittstellen Projekt halten. Insbesondere hatte ich eine CustomButton-Klasse in Gui, würde ich die ICustomButton-Schnittstelle in Schnittstellen halten.

Der Vorteil ist, dass jede Klasse, die ein ICustomButton braucht keinen Hinweis auf Gui brauchen selbst, sondern nur auf die viel leichtere Gewicht Schnittstellen Projekt.

Auch sollte eine Klasse in der Gui Projekt ändern und somit Ursache es, wieder aufgebaut werden nur die Projekte, die direkt auf die Custombutton Bezug Neuübersetzung müssten, während die, die auf die ICustomButton Bezug kann unberührt bleiben.

verstehe ich dieses Konzept, sondern sehen ein Problem:

Lets sagen, ich habe diese Schnittstelle:

public interface ICustomButton 
{ 
    void Animate(AnimatorStrategy strategy); 
} 

Wie Sie sehen können, es zu AnimatorStrategy bezieht, die eine konkrete Klasse ist und deshalb würde in einem anderen Projekt sitzen, können wir es Animation nennen. Nun muss sich das Schnittstellenprojekt auf Animation beziehen. Wenn dagegen Animation eine Schnittstelle verwendet, die in Schnittstellen definiert ist, muss darauf Bezug genommen werden.

Zyklische Abhängigkeit - "Hier kommen wir".

Die einzige Lösung für dieses Problem, die ich sehe, ist, dass alle in den Schnittstellen definierten Methoden Eingänge nehmen, die selbst Schnittstellen sind. Der Versuch, dies zu implementieren, wird höchstwahrscheinlich einen Domino-Effekt haben und schnell eine Schnittstelle erfordern, die selbst für die grundlegendsten Klassen implementiert wird.

Ich weiß nicht, ob ich mit diesem Aufwand in der Entwicklung umgehen möchte.

Irgendwelche Vorschläge?

Antwort

16

Vorsicht vor immer, immer, und nie - vor allem in der Nähe von allen, keiner oder jeder.

Sollten Sie immer setzen alle von Ihnen Schnittstellen in einer separaten Assembly? Nein - nicht unbedingt.

Sollten Sie Schnittstellen implementieren, die Sie von externen Benutzern Ihres Codes erwarten - möglicherweise. Ich würde Schnittstellen in eine externe Assembly einfügen, wenn Sie erwarten, dass sich mehrere Assemblies in Ihrem Projekt darauf verlassen - dies kann helfen, Kopplungsabhängigkeiten aufzubrechen. Ich habe diese Praxis auch verwendet, um zirkuläre Referenzprobleme zu lösen, bei denen Assemblies Schnittstellen untereinander kennen müssen.

Ich lege keine Interfaces an, die nur intern in einem Projekt in einer separaten Baugruppe verwendet werden. Ich propagiere auch keine Interfaces in die eigene Assembly, wenn meine Projekte relativ klein sind - oder die Interfaces nicht ohne die Assembly, die von ihnen abhängt, verwendet werden sollen.

Wie für das Beispiel, das Sie vorgestellt haben - ich würde vorschlagen, dass Sie nicht in Betracht ziehen Klassen in Ihrem System von Schnittstellen verweisen. Wann immer es möglich ist, versuche ich, Schnittstellen nur auf andere Schnittstellen zu beziehen - das hält die Dinge relativ entkoppelt. Sie können dies nicht immer erreichen - und wenn Sie diese Arten von Schnittstellen haben, müssen Sie sie mit der Baugruppe halten, von der sie abhängen.

Wenn Sie sich entschließen, Schnittstellen in eine separate Baugruppe zu integrieren, sollten Sie diese nicht unbedingt in einer Baugruppe zusammenfassen. Vielleicht möchten Sie sie durch ihre beabsichtigte Verwendung auseinandernehmen - auf diese Weise können Verbraucher nur die Schnittstellen einbeziehen, die für eine bestimmte Funktionsdomäne relevant sind.

+0

Danke für Ihre Antwort. Ich habe versucht, den DIP (Dependency Inversion Principle) zu verstehen. Während es in der Theorie nett aussieht (niedrigere Klassen sind abhängig von höheren Klassen und nicht umgekehrt), ist es in der Praxis schwer zu implementieren und zu pflegen. Es ist so viel einfacher, Schnittstellen einfach mit den Klassen zusammen zu halten, die sie implementieren. Ich denke, in diesem Fall stimme ich deinem Vorschlag zu, den Entwurf nur dann auf diese Weise zu "komplizieren", wenn es notwendig wird. –

3

Die einfache Antwort ist AnimatorStrategy sollte auch eine Schnittstelle sein. Die Implementierung für AnimatorStrategy befindet sich im Animationsprojekt, aber die Schnittstelle befindet sich in einem AnimationInterfaces-Projekt oder einem Interface-Projekt (nach Bedarf).

Ich hätte nicht unbedingt ein einzelnes Interface-Projekt, sondern eines für jede funktionale Gruppierung. Clients von Animation würden auf AnimationInterfaces verweisen, und Clients von Gui würden auf GuiInterfaces verweisen.

Auf diese Weise erhalten Sie Ihre Verträge, ohne irgendeine Implementierung zu mischen.

Für was es wert ist, bevorzuge ich umgekehrt mit Namenskonventionen zu gehen. Setzen Sie die Schnittstellen in Animation und die Implementierungen in AnimationImpl (oder ähnlich).Auf diese Weise verweisen Sie auf den Funktionsnamen.

-2

Was ist mit der Definition einer Schnittstelle mit Methoden, die generische Typen verwenden? Z.B.

public interface IThing 
{ 
    int DoMyThing<T>(T variable); 
} 

Wenn Sie einige Einschränkungen auf T variable benötigen, könnten Sie tun:

int DoMyThing<T>(T variable) where T : IOtherInterface; 

Wo IOtherInterface auch in Ihrem Schnittstellen Projekt definiert ist. Solange Ihre bestimmte Klasse von IThing und IOtherInterface erbt, können Sie dann die DoMyThing-Methode verwenden und eine Instanz Ihrer bestimmten Klasse übergeben, ohne eine zirkuläre Abhängigkeit zu haben. Ihr Code werden könnten:

public interface ICustomButton 
{ 
    void Animate<T>(T strategy) where T : IAnimateable; 
} 

Die Schnittstelle hat keinen Hinweis auf konkrete Klasse AnimatorStrategy.

+1

Wie ist das relevant? O_o –

+1

Das ist eine gute Idee, aber sieht eher aus wie ein Hack für mich. Schnittstellen sind da, um Verträge zu formulieren. Wie beschreibt T, welche Art von Strategie zu erwarten ist? Wenn ich IAnimateable hätte, würde ich T. nicht brauchen. –

1

Ich denke, dass zyklische Abhängigkeiten unvermeidlich sein können, selbst wenn Sie alles als Schnittstellen deklarieren, da es möglicherweise eine Methode in Assembly Foo gibt, die einen Parameter IBar nimmt, und eine Methode in Assembly Bar, die einen Parameter IFoo nimmt.

Ich würde sagen, dass es kein universelles Rezept dafür gibt, aber Sie sollten stattdessen "gesunden Menschenverstand" verwenden, um die Schnittstellen in Baugruppen nach Bedarf zu teilen.