Crie muitas classes similares ou apenas uma

4

O objetivo é criar um aplicativo que tenha objetos que possam representar algumas operações (adicionar, subtrair etc.).

Todos esses objetos terão funções e membros comuns e, portanto, implementarão uma interface ou herdarão de uma classe abstrata (que seria uma prática melhor, isso será em C # se isso for importante?).

Tanto quanto eu posso ver, existem duas maneiras diferentes de organizar todas essas classes.

  1. Eu poderia criar uma classe de adição, uma classe de subtração, etc. Isso tem a vantagem de ser altamente modular, mas a diferença entre as classes é mínima.
  2. Eu poderia criar uma classe e ter um membro que diria que tipo de operação está sendo representada. Isso significa muitas declarações de switch e perda de modularidade, além de ser mais difícil de manter.

Qual é a melhor prática? Existe uma maneira melhor de fazer isso não está listado acima? Se for importante, a lista de funções que devem ser suportadas é longa.

    
por soandos 31.05.2012 / 06:50
fonte

3 respostas

8

Em um mundo orientado a objetos dominado por linguagens como Java e C #, a opção 1 é o caminho a percorrer. Existem diferentes maneiras de modelar seus dados, por exemplo, uma outra maneira é com uma árvore que pode ser percorrida. Mas, seguindo o espírito da sua pergunta, há duas abordagens a serem consideradas. Em ambos os casos, é importante usar interfaces. Escrever programas sobre abstrações é longe mais flexível do que escrevê-las em implementações concretas.

A primeira abordagem é extrair código comum em super classes abstratas. Isso é realmente fácil de fazer com linguagens como Java e C #, e à primeira vista parece a escolha óbvia. O principal problema com essa abordagem é que é muito fácil confundir herança com as abstrações que você está tentando criar. Eu argumento que essas classes abstratas criadas inteiramente para compartilhar código comum, mas de outra forma sem importância para herança, fazem parte da implementação e, portanto, não devem ser confiadas no código do cliente. Isso pode ou não ser um problema, mas em ambos os casos não há nada que possa ser feito para impedir que isso aconteça.

Para remediar isso, a segunda abordagem seria favorecer a composição em relação às classes abstratas. Embora isso não seja tão fácil de implementar quanto o primeiro, ele resolve o problema deixado pelas classes abstratas. Ou seja, não pode haver confiança nos artefatos deixados pelos detalhes de implementação das classes abstratas, contanto que você encapsule adequadamente os objetos compostos. Se, no entanto, as classes abstratas (se elas precisarem ser abstratas) forem importantes para a hierarquia de herança e, portanto, forem mais do que apenas "buckets de código comuns", favorecer a composição reduzirá a utilidade das classes.

No final, acho que depende se as classes abstratas são importantes para construir uma abstração apropriada com base na herança, ou se o código comum está lá apenas para aliviar o fardo de mantê-lo. Pode ser muito chato de implementar se houver muitos métodos que apenas delegam a um objeto encapsulado, mas essa compensação pode valer a pena para algo mais puro. No entanto, algumas linguagens como o Ruby facilitam o suporte à composição com seus módulos, que são basicamente mixins .

    
por 31.05.2012 / 07:33
fonte
11

Depende de quanto essas classes têm em comum:

  • Absolutamente nada - crie um monte de classes autônomas.
  • Eles têm os mesmos métodos, mas os implementam de forma diferente - crie uma interface e várias classes que a implementem.
  • Eles compartilham algumas das principais funcionalidades, mas outras funcionalidades diferem - crie uma classe base e obtenha suas classes a partir disso.

Dependendo da complexidade de suas aulas, você pode querer ter mais de um nível em sua hierarquia de classes se você escolher a última opção.

Há também uma quarta opção, que é especialmente viável se toda a sua classe precisa ter uma operação: ter uma abordagem de programação funcional e dispensar as classes. Em vez disso, implemente uma função para cada operação (em C #, isso terá que ser um método estático em algum lugar, ou você terá que retornar lambdas de um método de fábrica, já que a linguagem não possui funções livres) e passá-las. Muito provavelmente, você deseja declarar um tipo de delegado para isso.

    
por 31.05.2012 / 07:59
fonte
2

.3. Uma classe base, substituindo implementações onde necessário em classes filho.

Por quê? Razão principal: Quaisquer alterações necessárias só acontecem em um único local e se propagam para todos os dependentes.

    
por 31.05.2012 / 07:10
fonte