OK .. nach all den Diskussionen ändere ich meine Frage ein wenig, um ein konkretes Beispiel besser wiederzugeben, mit dem ich mich befasse.
Ich habe zwei Klassen ModelOne
und ModelTwo
, Diese Klassen führen ähnliche Arten von 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 implementiert sind und ModelTwo
gemäß den Vorgaben ausgeklammert wurden DRY
. Die beiden Modelle werden innerhalb der ModelMain
Klasse instanziiert (die selbst auf einer höheren Ebene instanziiert wird usw. - aber ich höre auf dieser Ebene auf).
Der von mir verwendete IoC-Container ist Microsoft Unity . Ich gebe nicht vor, ein Experte zu sein, aber ich verstehe es so, dass Sie ein Tupel von Interface und Klasse für den Container registrieren. Wenn Sie eine konkrete Klasse wünschen, fragen Sie den IoC-Container nach dem Objekt, das mit einem bestimmten Interface übereinstimmt. Dies bedeutet, dass für jedes Objekt, das ich aus Unity instanziieren möchte, eine passende Schnittstelle vorhanden sein muss. Da jede meiner Klassen unterschiedliche (und nicht überlappende) Funktionen ausführt, gibt es ein 1: 1-Verhältnis zwischen Schnittstelle und Klasse 1 . Es bedeutet jedoch nicht, dass ich eine Schnittstelle für jede Klasse, die ich schreibe, sklavisch schreibe.
Also, was den Code betrifft, erhalte ich 2 :
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 meine Lösung zu organisieren ist. Soll ich die Klasse und die Schnittstelle zusammenhalten? Oder sollte ich Klassen und Interfaces zusammenhalten? Z.B:
Option 1 - Nach Klassennamen organisiert
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 Funktionen (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 - Weiteres Funktionsbeispiel
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 (und das ist möglicherweise umstritten) zum Verhältnis 1: 1 tendiere, hat dies Vorteile darin, die Beziehung zwischen den Dateien zu sehen.
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, eins dem anderen vorzuziehen? Oder andere mögliche Layouts, die ich verpasst habe?
1. Frank machte einen Kommentar, dass das Verhältnis von 1: 1 auf C ++ - Tage mit .h- und .cpp-Dateien zurückgeht. Ich weiß, woher er kommt. Mein Verständnis von Einigkeit scheint mich in diese Ecke zu führen, aber ich bin mir auch nicht sicher, wie ich aus ihr herauskommen soll, wenn Sie auch dem Sprichwort von Program to an interface
folgen. Aber das ist eine Diskussion für einen weiteren Tag.
2. Ich habe die Details jedes Objektkonstruktors ausgelassen. Hier injiziert der IoC-Container Objekte nach Bedarf.
Client1
Bedarf IBase
eine Derived1
. Bei Client2
Bedarf IBase
bietet die IoC eine Derived2
.
interface
. Eine interface
ist wirklich nur eine abstrakte Klasse mit allen virtuellen Mitgliedern.