OK ... después de toda la discusión, estoy cambiando mi pregunta ligeramente para reflejar mejor un ejemplo concreto con el que estoy tratando.
Tengo dos clases ModelOne
y ModelTwo
, estas clases realizan un tipo similar de funcionalidad pero no están relacionadas entre sí. Sin embargo, tengo una tercera clase CommonFunc
que contiene algunas funciones públicas que se implementa en tanto ModelOne
y ModelTwo
y ha sido un factor fuera de acuerdo DRY
. Los dos modelos se instancian dentro de la ModelMain
clase (que a su vez se instancia en un nivel superior, etc., pero me detengo en este nivel).
El contenedor de IoC que estoy usando es Microsoft Unity . No pretendo ser un experto en él, pero entiendo que registras una tupla de interfaz y clase con el contenedor y cuando quieres una clase concreta le preguntas al contenedor IoC por cualquier objeto que coincida con una interfaz específica. Esto implica que para cada objeto que quiero instanciar desde Unity, debe haber una interfaz coincidente. Debido a que cada una de mis clases realiza una funcionalidad diferente (y no superpuesta), esto significa que hay una relación 1: 1 entre la interfaz y la clase 1 . Sin embargo, no significa que estoy escribiendo servilmente una interfaz para cada clase que escribo.
Así, en cuanto al código, termino con 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 pregunta es sobre cómo organizar mi solución. ¿Debo mantener la clase y la interfaz juntas? ¿O debería mantener las clases y las interfaces juntas? P.EJ:
Opción 1 : organizada por nombre de clase
MySolution
|
|-MyProject
| |
|-Models
| |
|-Common
| |
| |-CommonFunc.cs
| |-ICommonFunc.cs
|
|-Main
| |
| |-IModelMain.cs
| |-ModelMain.cs
|
|-One
| |
| |-IModelOne.cs
| |-ModelOne.cs
|
|-Two
|
|-IModelTwo.cs
|-ModelTwo.cs
|
Opción 2 : organizada por funcionalidad (principalmente)
MySolution
|
|-MyProject
| |
|-Models
| |
|-Common
| |
| |-CommonFunc.cs
| |-ICommonFunc.cs
|
|-IModelMain.cs
|-IModelOne.cs
|-IModelTwo.cs
|-ModelMain.cs
|-ModelOne.cs
|-ModelTwo.cs
|
Opción 3 - Interfaz de separación e implementación
MySolution
|
|-MyProject
|
|-Interfaces
| |
| |-Models
| | |
| |-Common
| | |-ICommonFunc.cs
| |
| |-IModelMain.cs
| |-IModelOne.cs
| |-IModelTwo.cs
|
|-Classes
|
|-Models
| |
|-Common
| |-CommonFunc.cs
|
|-ModelMain.cs
|-ModelOne.cs
|-ModelTwo.cs
|
Opción 4 : llevar el ejemplo de funcionalidad más allá
MySolution
|
|-MyProject
| |
|-Models
| |
|-Components
| |
| |-Common
| | |
| | |-CommonFunc.cs
| | |-ICommonFunc.cs
| |
| |-IModelOne.cs
| |-IModelTwo.cs
| |-ModelOne.cs
| |-ModelTwo.cs
|
|-IModelMain.cs
|-ModelMain.cs
|
No me gusta la opción 1 debido al nombre de la clase en la ruta. Pero como estoy tendiendo a una relación 1: 1 debido a mi elección / uso de IoC (y eso puede ser discutible), esto tiene ventajas al ver la relación entre los archivos.
La opción 2 me resulta atractiva, pero ahora he enturbiado las aguas entre el ModelMain
y los submodelos.
La opción 3 funciona para separar la definición de interfaz de la implementación, pero ahora tengo estos descansos artificiales en los nombres de ruta.
Opción 4. Tomé la Opción 2 y la ajusté para separar los componentes del modelo principal.
¿Hay una buena razón para preferir uno sobre el otro? ¿O cualquier otro diseño potencial que me haya perdido?
1. Frank hizo un comentario de que tener una relación 1: 1 se remonta a los días C ++ de los archivos .h y .cpp. Sé de dónde viene. Mi comprensión de la Unidad parece ponerme en este rincón, pero tampoco estoy seguro de cómo salir de ella si también sigues el adagio de Program to an interface
Pero esa es una discusión para otro día.
2. He omitido los detalles de cada constructor de objetos. Aquí es donde el contenedor de IoC inyecta objetos según sea necesario.
Client1
necesita un IBase
, proporciona un Derived1
. Cuando Client2
necesita un IBase
, el IoC proporciona un Derived2
.
interface
. An interface
es realmente solo una clase abstracta con todos los miembros virtuales.