it-swarm.com.de

Wie organisiere ich Klassen- und Schnittstellendateien am besten?

OK ... nach all der Diskussion ändere ich meine Frage leicht, um ein konkretes Beispiel, mit dem ich mich befasse, besser wiederzugeben.


Ich habe zwei Klassen ModelOne und ModelTwo. Diese Klassen führen ähnliche Funktionen aus, sind jedoch nicht miteinander verbunden. Ich habe jedoch eine dritte Klasse CommonFunc, die einige öffentliche Funktionen enthält, die sowohl in ModelOne als auch in ModelTwo implementiert sind und gemäß DRY herausgerechnet wurden. Die beiden Modelle werden innerhalb der Klasse ModelMain instanziiert (die selbst auf einer höheren Ebene usw. instanziiert wird - aber ich halte auf dieser Ebene an).

Der von mir verwendete IoC-Container ist Microsoft Unity . Ich gebe nicht vor, ein Experte zu sein, aber ich verstehe, dass Sie ein Tupel von Schnittstelle und Klasse beim Container registrieren und wenn Sie eine konkrete Klasse möchten, fragen Sie den IoC-Container nach dem Objekt, das zu einer bestimmten Schnittstelle passt. Dies bedeutet, dass für jedes Objekt, das ich von Unity aus instanziieren möchte, eine passende Schnittstelle vorhanden sein muss. Da jede meiner Klassen unterschiedliche (und nicht überlappende) Funktionen ausführt, bedeutet dies, dass zwischen Schnittstelle und Klasse ein Verhältnis von 1: 1 besteht1. Dies bedeutet jedoch nicht, dass ich sklavisch eine Schnittstelle für jede Klasse schreibe, die ich schreibe.

Also am Ende habe ich Code2::

public interface ICommonFunc 
{ 
}

public interface IModelOne 
{ 
   ICommonFunc Common { get; } 
   .. 
}

public interface IModelTwo
{ 
   ICommonFunc Common { get; } 
   .. 
}

public interface IModelMain 
{ 
  IModelOne One { get; } 
  IModelTwo Two { get; } 
  ..
}

public class CommonFunc : ICommonFunc { .. }

public class ModelOne : IModelOne { .. }

public class ModelTwo : IModelTwo { .. }

public class ModelMain : IModelMain { .. }

Die Frage ist, wie ich meine Lösung organisieren kann. Soll ich die Klasse und die Schnittstelle zusammenhalten? Oder sollte ich Klassen und Schnittstellen zusammenhalten? Z.B:

Option 1 - Organisiert nach Klassennamen

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Common
          |   |
          |   |-CommonFunc.cs
          |   |-ICommonFunc.cs
          |
          |-Main
          |   |
          |   |-IModelMain.cs
          |   |-ModelMain.cs
          |
          |-One
          |   |
          |   |-IModelOne.cs
          |   |-ModelOne.cs
          |
          |-Two
              |
              |-IModelTwo.cs
              |-ModelTwo.cs
              |

Option 2 - Organisiert nach Funktionalität (meistens)

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Common
          |   |
          |   |-CommonFunc.cs
          |   |-ICommonFunc.cs
          |
          |-IModelMain.cs
          |-IModelOne.cs
          |-IModelTwo.cs
          |-ModelMain.cs
          |-ModelOne.cs
          |-ModelTwo.cs
          |

Option 3 - Schnittstelle und Implementierung trennen

MySolution
  |
  |-MyProject
      |
      |-Interfaces
      |   |
      |   |-Models
      |   |   |
      |       |-Common
      |       |   |-ICommonFunc.cs
      |       |
      |       |-IModelMain.cs
      |       |-IModelOne.cs
      |       |-IModelTwo.cs
      |
      |-Classes
          | 
          |-Models
          |   |
              |-Common
              |   |-CommonFunc.cs
              |
              |-ModelMain.cs
              |-ModelOne.cs
              |-ModelTwo.cs
              |

Option 4 - Das Funktionsbeispiel weiterführen

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Components
          |   |
          |   |-Common
          |   |   |
          |   |   |-CommonFunc.cs
          |   |   |-ICommonFunc.cs
          |   |   
          |   |-IModelOne.cs
          |   |-IModelTwo.cs
          |   |-ModelOne.cs
          |   |-ModelTwo.cs
          |
          |-IModelMain.cs
          |-ModelMain.cs
          |

Ich mag Option 1 wegen des Klassennamens im Pfad nicht. Da ich jedoch aufgrund meiner IoC-Auswahl/-Verwendung zum Verhältnis 1: 1 tendiere (und das möglicherweise umstritten ist), hat dies Vorteile, wenn ich die Beziehung zwischen den Dateien sehe.

Option 2 gefällt mir, aber jetzt habe ich das Wasser zwischen dem ModelMain und den Untermodellen getrübt.

Option 3 trennt die Schnittstellendefinition von der Implementierung, aber jetzt habe ich diese künstlichen Unterbrechungen in den Pfadnamen.

Option 4. Ich habe Option 2 gewählt und optimiert, um die Komponenten vom übergeordneten Modell zu trennen.

Gibt es einen guten Grund, einander vorzuziehen? Oder andere mögliche Layouts, die ich verpasst habe?


1. Frank machte einen Kommentar, dass das Verhältnis 1: 1 auf C++ - Tage mit .h- und .cpp-Dateien zurückgeht. Ich weiß, woher er kommt. Mein Verständnis von Einheit scheint mich in diese Ecke zu bringen, aber ich bin mir auch nicht sicher, wie ich daraus herauskommen soll, wenn Sie auch dem Sprichwort von Program to an interface Folgen. Aber das ist eine Diskussion für einen anderen Tag.

2. Ich habe die Details jedes Objektkonstruktors weggelassen. Hier fügt der IoC-Container nach Bedarf Objekte ein.

18
Peter M

Da eine Schnittstelle einer Basisklasse abstrakt ähnlich ist, verwenden Sie dieselbe Logik, die Sie für eine Basisklasse verwenden würden. Klassen, die eine Schnittstelle implementieren, sind eng mit der Schnittstelle verbunden.

Ich bezweifle, dass Sie ein Verzeichnis namens "Basisklassen" bevorzugen würden; Die meisten Entwickler würden das nicht wollen, noch ein Verzeichnis namens "Interfaces". In c # sind Verzeichnisse standardmäßig auch Namespaces, was sie doppelt verwirrt.

Der beste Ansatz besteht darin, darüber nachzudenken, wie Sie die Klassen/Schnittstellen aufteilen würden, wenn Sie einige in eine separate Bibliothek stellen und die Namespaces/Verzeichnisse auf ähnliche Weise organisieren müssten. Die .net Framework Design-Richtlinien enthalten Namespace-Vorschläge , was hilfreich sein kann.

5
Frank Hileman

Ich gehe den zweiten Weg, natürlich mit einigen weiteren Ordnern/Namespaces in komplexen Projekten. Das bedeutet, dass ich die Schnittstellen von den konkreten Implementierungen entkopple.

In einem anderen Projekt müssen Sie möglicherweise die Schnittstellendefinition kennen, es ist jedoch überhaupt nicht erforderlich, eine konkrete Klasse zu kennen, die sie implementiert - insbesondere, wenn Sie einen IoC-Container verwenden. Diese anderen Projekte müssen also nur auf die Schnittstellenprojekte verweisen, nicht auf die Implementierungsprojekte. Dies kann Referenzen niedrig halten und Zirkelreferenzprobleme verhindern.

1
Bernhard Hiller

Wie immer bei jeder Designfrage müssen Sie zunächst feststellen, wer Ihre Benutzer sind. Wie werden sie Ihre Organisation nutzen?

Sind es Programmierer, die Ihre Klassen verwenden werden? Dann ist es möglicherweise am besten, die Schnittstellen vom Code zu trennen.

Sind sie Betreuer Ihres Codes? Dann halten Sie die Schnittstellen und Klassen am besten zusammen.

Setzen Sie sich und erstellen Sie einige Anwendungsfälle. Dann kann die beste Organisation vor Ihnen erscheinen.

1
shawnhcorey