Como melhor organizar arquivos de classe e interface?

5

OK .. depois de toda a discussão, estou mudando um pouco a minha pergunta para refletir melhor um exemplo concreto com o qual estou lidando.

Eu tenho duas classes ModelOne e ModelTwo , Essas classes executam um tipo similar de funcionalidade, mas não estão relacionadas umas com as outras. No entanto, tenho uma terceira classe CommonFunc que contém alguma funcionalidade pública que é implementada em ModelOne e ModelTwo e foi fatorada como DRY . Os dois modelos são instanciados na classe ModelMain (que é instanciada em um nível mais alto, etc - mas estou parando nesse nível).

O contêiner IoC que estou usando é Microsoft Unity . Eu não pretendo ser um expert nisso, mas meu entendimento é que você registra uma tupla de interface e classe com o container e quando você quer uma classe concreta, você pergunta ao container IoC por qualquer objeto que corresponda a uma interface específica. Isso implica que, para cada objeto que eu queira instanciar do Unity, tem que haver uma interface correspondente. Como cada uma das minhas classes executa uma funcionalidade diferente (e não sobreposta), isso significa que há uma proporção de 1: 1 entre a interface e a classe 1 . No entanto, isso não significa que eu esteja servilmente escrevendo uma interface para cada classe que escrevo.

Assim, código sábio eu acabo 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 interface juntos? Ou devo manter as classes e interfaces juntas? EG:

Opção 1 - organizada por nome 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
              |

Opção 2 - organizada 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 de separação

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 - Continuando com o exemplo da funcionalidade

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 para a proporção 1: 1 por causa da minha escolha / uso de IoC (e que pode ser debatível) isso tem vantagens em ver a relação entre os arquivos.

A opção 2 é atraente para mim, mas agora estou enlameando as águas entre o ModelMain e os submodelos.

A opção 3 funciona para separar a definição da interface da implementação, mas agora tenho essas interrupções artificiais nos nomes dos caminhos.

Opção 4. Eu peguei a Opção 2 e ajustei-a para separar os componentes do modelo pai.

Existe uma boa razão para preferir um ao outro? Ou qualquer outro layout em potencial que eu tenha perdido?

1. Frank fez um comentário dizendo que ter uma proporção de 1: 1 remete aos dias de C ++ dos arquivos .h e .cpp. Eu sei de onde ele vem. Meu entendimento de Unity parece me colocar nesse canto, mas também não tenho certeza de como sair dele se você também está seguindo o provérbio de Program to an interface Mas isso é uma discussão para outro dia.

2. Eu deixei de fora os detalhes de cada construtor de objetos. É aqui que o contêiner IoC injeta objetos conforme necessário.

    
por Peter M 26.07.2017 / 22:35
fonte

3 respostas

4

Como uma interface é abstratamente semelhante a uma classe base, use a mesma lógica que você usaria para uma classe base. As classes que implementam uma interface estão intimamente relacionadas à interface.

Eu duvido que você prefira um diretório chamado "Base Classes"; a maioria dos desenvolvedores não gostaria disso, nem um diretório chamado "Interfaces". Em c #, os diretórios também são namespaces por padrão, tornando-se duplamente confuso.

A melhor abordagem é pensar em como você dividiria as classes / interfaces se tivesse que colocar algumas em uma biblioteca separada e organizar os namespaces / diretórios de maneira semelhante. As diretrizes de design da estrutura .net têm sugestões de namespace que podem ser útil.

    
por 27.07.2017 / 00:53
fonte
1

Eu tomo a segunda abordagem, com mais pastas / namespaces em projetos complexos, é claro. Isso significa que eu desunho as interfaces das implementações concretas.

Em um projeto diferente, talvez seja necessário conhecer a definição da interface, mas não é necessário conhecer qualquer classe concreta implementando-a - especialmente quando você usa um contêiner IoC. Portanto, esses outros projetos precisam apenas referenciar os projetos de interface, não os projetos de implementação. Isso pode manter as referências baixas e evitar problemas de referência circular.

    
por 27.07.2017 / 11:08
fonte
1

Como sempre, com qualquer questão de design, a primeira coisa a fazer é determinar quem são seus usuários. Como eles vão usar sua organização?

Eles são codificadores que usarão suas aulas? Em seguida, separar as interfaces do código pode ser melhor.

Eles são mantenedores do seu código? Em seguida, mantenha as interfaces e as classes juntas pode ser melhor.

Sente-se e crie alguns casos de uso. Então a melhor organização pode aparecer antes de você.

    
por 30.07.2017 / 03:36
fonte