Digamos que estamos fazendo um analisador. Uma implementação pode ser:
public sealed class Parser1
{
public string Parse(string text)
{
...
}
}
Ou podemos passar o texto para o construtor:
public sealed class Parser2
{
public Parser2(string text)
{
this.text = text;
}
public string Parse()
{
...
}
}
O uso é simples nos dois casos, mas o que significa habilitar a entrada de parâmetro em Parser1
comparação com o outro? Que mensagem eu enviei a um colega programador quando ele olha para a API? Além disso, existem vantagens / desvantagens técnicas em certos casos?
Outra pergunta surge quando percebo que uma interface não teria sentido na segunda implementação:
public interface IParser
{
string Parse();
}
... onde uma interface na primeira poderia servir a pelo menos algum propósito. Isso significa algo em particular, que uma classe é "interfacável" ou não?
object-oriented
interfaces
methods
construction
ciscoheat
fonte
fonte
Respostas:
Semanticamente falando, no OOP, você deve passar apenas ao construtor um conjunto de parâmetros necessários para criar a classe - da mesma forma que quando você chama um método, deve passar apenas os parâmetros necessários para executar sua lógica de negócios.
Os parâmetros que você precisa passar no construtor são parâmetros que não possuem valor padrão sensível e se sua classe é imutável (ou mesmo a
struct
), então todas as propriedades padrão não-passadas devem ser passadas.Em relação aos seus dois exemplos:
text
para o construtor, isso sugere que aParser2
classe será criada especificamente para analisar essa instância do texto posteriormente. Será um analisador específico. Normalmente, esse processo ocorre quando a construção da classe é muito cara ou sutil; um RegEx pode ser compilado no construtor; portanto, depois de manter uma instância, você pode reutilizá-la sem ter que pagar o custo da compilação; outro exemplo é a inicialização do PRNG - é melhor se isso for feito raramente.text
para o método, ele sinaliza queParser1
pode ser reutilizado para analisar diferentes textos por chamadas.fonte
Bem, vamos lembrar o que significa passar uma variável como parâmetro construtor: Você inicializa um objeto para usar suas variáveis de instância nos métodos do objeto. O ponto é que você provavelmente deseja usá-lo em mais de um método, pois deseja ter uma alta coesão em sua classe.
Passar um parâmetro diretamente para um método significa, de certa forma, enviar uma mensagem para um objeto e provavelmente receber uma resposta. Com isso, o cliente deseja que o objeto entregue um serviço para ele.
Portanto, concluindo, esses são dois meios muito diferentes de passar parâmetros e você deve escolher se seu objeto deve fornecer um serviço ou fornecer alguma funcionalidade inerentemente enquanto gerencia algumas informações internamente.
fonte
É uma mudança fundamental no design. E o design deve transmitir intenção e significado. Você precisa ter objetos separados para cada string que deseja analisar? Em outras palavras, por que precisamos de uma instância do analisador com stringX e outra instância com stringY? O que há com a análise e a sequência especificada de que os dois devem viver e morrer juntos? Assumindo que a "implementação [de análise] subjacente" (como diz Robert Harvey) não muda, parece não haver sentido. E mesmo assim é questionável IMHO.
Os parâmetros do construtor dizem que essas coisas são necessárias para um objeto. O estado adequado não é garantido sem eles. Além disso, sei como / por que um analisador é fundamentalmente diferente de outro.
Os parâmetros do construtor me impedem de saber muito sobre como usar a classe. Se, em vez disso, devo definir determinadas propriedades - como eu sei disso? Uma lata inteira de vermes se abre. Quais propriedades? Em que ordem? Antes de usar quais métodos? e assim por diante.
Uma interface, como na API, são os métodos e propriedades expostos ao código do cliente. Não se embrulhe
public interface { ... }
exclusivamente. Portanto, o significado da interface está no dilema de parâmetro entre o construtor e o método vs, NOTpublic interface Iparser
vspublic sealed class Parser
A
sealed
turma é estranha. Se eu estou pensando em diferentes implementações de analisador - você mencionou "Iparser" -, herança é meu primeiro pensamento. É apenas uma extensão conceitual natural do meu pensamento. Ou seja, todos osParserX
s são fundamentalmenteParser
s. Como mais dizer isso? ... Um Shepard alemão é um cachorro (herança), mas eu posso treinar meu papagaio para latir (agir como um cachorro - "interface"); mas Polly não é um cachorro, apenas fingindo, tendo aprendido um subconjunto de dogness. Classes, abstratas ou não, servem perfeitamente bem como interfaces .fonte
A segunda versão da classe pode ser imutável.
A interface ainda pode ser usada para fornecer a capacidade de trocar a implementação subjacente.
fonte
Parser1
Construir com um construtor padrão e passar o texto de entrada para um método implica que o Parser1 é reutilizável.
Parser2
Passar o texto de entrada para o construtor implica que um novo Parser2 deve ser criado para cada sequência de entrada.
fonte