OK .. après toute la discussion, je modifie légèrement ma question pour mieux refléter un exemple concret que je traite.
J'ai deux classes ModelOne
et ModelTwo
, ces classes exécutent un type de fonctionnalité similaire mais ne sont pas liées l'une à l'autre. Cependant , j'ai une troisième classe CommonFunc
qui contient certaines fonctionnalités du public qui est mis en œuvre à la fois ModelOne
et ModelTwo
et a été pris en compte comme par DRY
. Les deux modèles sont instanciés au sein de la ModelMain
classe (qui elle-même est instanciée à un niveau supérieur, etc. - mais je m'arrête à ce niveau).
Le conteneur IoC que j'utilise est Microsoft Unity . Je ne prétends pas être un expert en la matière, mais d'après ce que je comprends, vous enregistrez un tuple d'interface et de classe avec le conteneur et lorsque vous voulez une classe concrète, vous demandez au conteneur IoC quel que soit l'objet correspondant à une interface spécifique. Cela implique que pour chaque objet que je veux instancier depuis Unity, il doit y avoir une interface correspondante. Étant donné que chacune de mes classes exécute des fonctionnalités différentes (et sans chevauchement), cela signifie qu'il existe un rapport 1: 1 entre l'interface et la classe 1 . Cependant, cela ne signifie pas que j'écris servilement une interface pour chaque classe que j'écris.
Ainsi, au niveau du code, je me retrouve avec 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 { .. }
La question est de savoir comment organiser ma solution. Dois-je garder la classe et l'interface ensemble? Ou dois-je garder les classes et les interfaces ensemble? PAR EXEMPLE:
Option 1 - Organisé par nom de classe
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 - Organisé par fonctionnalité (principalement)
MySolution
|
|-MyProject
| |
|-Models
| |
|-Common
| |
| |-CommonFunc.cs
| |-ICommonFunc.cs
|
|-IModelMain.cs
|-IModelOne.cs
|-IModelTwo.cs
|-ModelMain.cs
|-ModelOne.cs
|-ModelTwo.cs
|
Option 3 - Interface et mise en œuvre séparées
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 - Aller plus loin dans l'exemple de fonctionnalité
MySolution
|
|-MyProject
| |
|-Models
| |
|-Components
| |
| |-Common
| | |
| | |-CommonFunc.cs
| | |-ICommonFunc.cs
| |
| |-IModelOne.cs
| |-IModelTwo.cs
| |-ModelOne.cs
| |-ModelTwo.cs
|
|-IModelMain.cs
|-ModelMain.cs
|
Je déteste en quelque sorte l'option 1 à cause du nom de classe dans le chemin. Mais comme je tend à un rapport de 1: 1 en raison de mon choix / utilisation d'IoC (et cela peut être discutable), cela a des avantages à voir la relation entre les fichiers.
L'option 2 me plaît, mais maintenant j'ai brouillé les eaux entre le ModelMain
et les sous-modèles.
L'option 3 fonctionne pour séparer la définition de l'interface de l'implémentation, mais j'ai maintenant ces ruptures artificielles dans les noms de chemin.
Option 4. J'ai pris l'option 2 et l'ai modifiée afin de séparer les composants du modèle parent.
Y a-t-il une bonne raison de préférer l'un à l'autre? Ou toute autre disposition potentielle que j'ai manquée?
1. Frank a fait un commentaire selon lequel le rapport 1: 1 ramène aux jours C ++ des fichiers .h et .cpp. Je sais d'où il vient. Ma compréhension de l'Unité semble me mettre dans ce coin, mais je ne sais pas non plus comment m'en sortir si vous suivez également l'adage de Program to an interface
Mais c'est une discussion pour un autre jour.
2. J'ai omis les détails de chaque constructeur d'objets. C'est là que le conteneur IoC injecte des objets selon les besoins.
Client1
besoin d'un IBase
, il fournit un Derived1
. En cas de Client2
besoin IBase
, l'IoC fournit un Derived2
.
interface
. An interface
est vraiment juste une classe abstraite avec tous les membres virtuels.