OK .. depois de toda a discussão, estou mudando minha pergunta um pouco para refletir melhor um exemplo concreto com o qual estou lidando.
Eu tenho duas classes ModelOne
e ModelTwo
, essas classes executam um tipo semelhante de funcionalidade, mas não são relacionadas umas às outras. No entanto eu tenho uma terceira classe CommonFunc
que contém algumas funcionalidades pública que é implementado em ambos ModelOne
e ModelTwo
, tendo sido contabilizado como por DRY
. Os dois modelos são instanciados dentro da ModelMain
classe (que em si é instanciada em um nível superior, etc. - mas estou parando nesse nível).
O contêiner de IoC que estou usando é o Microsoft Unity . Não pretendo ser especialista nisso, mas entendo que você registra uma tupla de interface e classe no contêiner e quando deseja uma classe concreta, solicita ao contêiner IoC qualquer objeto que corresponda a uma interface específica. Isso implica que, para cada objeto que eu quero instanciar do Unity, deve haver uma interface correspondente. Como cada uma das minhas classes executa funcionalidades diferentes (e não sobrepostas), isso significa que existe uma proporção de 1: 1 entre a interface e a classe 1 . No entanto, isso não significa que estou escrevendo servilmente uma interface para cada classe que escrevo.
Assim, em termos de código, termino com 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 { .. }
A questão é sobre como organizar minha solução. Devo manter a classe e a interface juntas? Ou devo manter classes e interfaces juntas? POR EXEMPLO:
Opção 1 - Organizada pelo nome da classe
MySolution
|
|-MyProject
| |
|-Models
| |
|-Common
| |
| |-CommonFunc.cs
| |-ICommonFunc.cs
|
|-Main
| |
| |-IModelMain.cs
| |-ModelMain.cs
|
|-One
| |
| |-IModelOne.cs
| |-ModelOne.cs
|
|-Two
|
|-IModelTwo.cs
|-ModelTwo.cs
|
Opção 2 - Organizado por funcionalidade (principalmente)
MySolution
|
|-MyProject
| |
|-Models
| |
|-Common
| |
| |-CommonFunc.cs
| |-ICommonFunc.cs
|
|-IModelMain.cs
|-IModelOne.cs
|-IModelTwo.cs
|-ModelMain.cs
|-ModelOne.cs
|-ModelTwo.cs
|
Opção 3 - Interface e implementação separadas
MySolution
|
|-MyProject
|
|-Interfaces
| |
| |-Models
| | |
| |-Common
| | |-ICommonFunc.cs
| |
| |-IModelMain.cs
| |-IModelOne.cs
| |-IModelTwo.cs
|
|-Classes
|
|-Models
| |
|-Common
| |-CommonFunc.cs
|
|-ModelMain.cs
|-ModelOne.cs
|-ModelTwo.cs
|
Opção 4 - Levando o exemplo de funcionalidade adiante
MySolution
|
|-MyProject
| |
|-Models
| |
|-Components
| |
| |-Common
| | |
| | |-CommonFunc.cs
| | |-ICommonFunc.cs
| |
| |-IModelOne.cs
| |-IModelTwo.cs
| |-ModelOne.cs
| |-ModelTwo.cs
|
|-IModelMain.cs
|-ModelMain.cs
|
Eu meio que não gosto da opção 1 por causa do nome da classe no caminho. Mas como estou tendendo à proporção de 1: 1 por causa da minha escolha / uso de IoC (e isso pode ser discutível), isso tem vantagens em ver o relacionamento entre os arquivos.
A opção 2 é atraente para mim, mas agora turvo as águas entre ModelMain
os submodelos e os modelos.
A opção 3 funciona para separar a definição da interface da implementação, mas agora tenho essas quebras artificiais nos nomes dos caminhos.
Opção 4. Peguei a Opção 2 e a aprimorei para separar os componentes do modelo pai.
Há boas razões para preferir um ao outro? Ou outros layouts em potencial que eu perdi?
1. Frank fez um comentário de que a proporção de 1: 1 remete aos dias C ++ dos arquivos .h e .cpp. Eu sei de onde ele está vindo. Meu entendimento da Unity parece me colocar nesse canto, mas também não tenho certeza de como sair dela se você também estiver seguindo o ditado de Program to an interface
Mas isso é uma discussão para outro dia.
2. Deixei de fora os detalhes de cada construtor de objetos. É aqui que o contêiner de IoC injeta objetos, conforme necessário.
Client1
precisa de um IBase
, ele fornece um Derived1
. Quando Client2
precisa de um IBase
, o IoC fornece um Derived2
.
interface
por isso. Um interface
é realmente apenas uma classe abstrata com todos os membros virtuais.