Costumo encontrar esse problema, especialmente em Java, mesmo que eu ache que seja um problema geral de POO. Ou seja: criar uma exceção revela um problema de design.
Suponha que eu tenha uma classe que tenha um String name
campo e um String surname
campo.
Em seguida, ele usa esses campos para compor o nome completo de uma pessoa, a fim de exibi-lo em algum tipo de documento, digamos uma fatura.
public void String name;
public void String surname;
public String getCompleteName() {return name + " " + surname;}
public void displayCompleteNameOnInvoice() {
String completeName = getCompleteName();
//do something with it....
}
Agora, quero reforçar o comportamento da minha classe lançando um erro se displayCompleteNameOnInvoice
for chamado antes que o nome seja atribuído. Parece uma boa ideia, não é?
Eu posso adicionar um código de exceção ao getCompleteName
método. Mas dessa maneira estou violando um contrato "implícito" com o usuário da classe; em geral, os getters não devem lançar exceções se seus valores não estiverem definidos. Ok, este não é um getter padrão, pois não retorna um único campo, mas, do ponto de vista do usuário, a distinção pode ser sutil demais para pensar sobre isso.
Ou posso lançar a exceção de dentro do displayCompleteNameOnInvoice
. Mas, para fazer isso, eu deveria testar diretamente os campos name
ou surname
, violando a abstração representada por getCompleteName
. É responsabilidade deste método verificar e criar o nome completo. Pode até decidir, baseando a decisão em outros dados, que, em alguns casos, é suficiente surname
.
Portanto, a única possibilidade parece alterar a semântica do método getCompleteName
para composeCompleteName
, o que sugere um comportamento mais "ativo" e, com ele, a capacidade de gerar uma exceção.
Essa é a melhor solução de design? Estou sempre procurando o melhor equilíbrio entre simplicidade e correção. Existe uma referência de design para esse problema?
fonte
displayCompleteNameOnInvoice
basta lançar uma exceção segetCompleteName
retornarnull
, não é?Respostas:
Não permita que sua turma seja construída sem atribuir um nome.
fonte
A resposta fornecida pelo DeadMG praticamente se destaca, mas deixe-me expressá-la de maneira um pouco diferente: evite ter objetos com estado inválido. Um objeto deve ser "inteiro" no contexto da tarefa que ele realiza. Ou não deveria existir.
Portanto, se você quiser fluidez, use algo como o Padrão do Construtor . Ou qualquer outra coisa que seja uma reificação separada de um objeto em construção, em oposição à "coisa real", onde é garantido que este último possui um estado válido e é aquele que realmente expõe as operações definidas nesse estado (por exemplo
getCompleteName
).fonte
So if you want fluidity, use something like the builder pattern
- fluidez ou imutabilidade (a menos que seja isso que você queira dizer). Se alguém deseja criar objetos imutáveis, mas precisa adiar a definição de alguns valores, o padrão a seguir é defini-los um por um em um objeto construtor e criar apenas um imutável quando reunirmos todos os valores necessários para a correta estado ...Não tem um objeto com um estado inválido.
O objetivo de um construtor ou construtor é definir o estado do objeto como algo consistente e utilizável. Sem essa garantia, surgem problemas com o estado inicializado incorretamente nos objetos. Esse problema se agrava se você estiver lidando com simultaneidade e algo puder acessar o objeto antes de concluir a configuração.
Portanto, a pergunta para esta parte é "por que você está permitindo que o nome ou sobrenome seja nulo?" Se isso não é algo válido na classe para que funcione corretamente, não permita. Tenha um construtor ou construtor que valide adequadamente a criação do objeto e, se não estiver certo, levante o problema nesse ponto. Você também pode usar uma das
@NotNull
anotações existentes para ajudar a comunicar que "isso não pode ser nulo" e aplicá-la na codificação e análise.Com essa garantia, fica muito mais fácil argumentar sobre o código, o que ele faz e não precisar lançar exceções em locais estranhos ou fazer verificações excessivas nas funções getter.
Getters que fazem mais.
Há um pouco por aí sobre esse assunto. Você tem Quanto Logic em Getters e que deve ser permitido dentro getters e setters? daqui e getters e setters executando lógica adicional no Stack Overflow. Esse é um problema que surge repetidamente no design da classe.
O núcleo disso vem de:
E você está certo. Eles não deveriam. Eles devem parecer "burros" para o resto do mundo e fazer o que é esperado. Colocar muita lógica aí leva a questões em que a lei de menor espanto é violada. E vamos ser sinceros, você realmente não quer envolver os getters com um
try catch
porque sabemos o quanto amamos fazer isso em geral.Também existem situações em que você deve usar um
getFoo
método como a estrutura JavaBeans e quando você tem algo da chamada EL esperando obter um bean (para que<% bar.foo %>
realmente chamegetFoo()
na classe - deixando de lado o 'caso o bean esteja fazendo a composição ou deveria que são deixados à vista? ', porque é possível chegar facilmente a situações em que um ou outro pode ser claramente a resposta certa)Perceba também que é possível que um determinado getter seja especificado em uma interface ou faça parte da API pública exposta anteriormente para a classe que está sendo refatorada (uma versão anterior tinha apenas 'completeName' que foi retornado e uma refatoração foi interrompida em dois campos).
No fim do dia...
Faça o que é mais fácil de raciocinar. Você gastará mais tempo mantendo esse código do que gastando projetando-o (embora, quanto menos tempo você gaste projetando, mais tempo você gastará mantendo). A escolha ideal é projetá-lo de tal maneira que não demore muito tempo para mantê-lo, mas também não fique lá pensando por dias - a menos que essa seja realmente uma escolha de design que terá dias de implicações posteriormente .
Tentar manter a pureza semântica do ser getter
private T foo; T getFoo() { return foo; }
causará problemas em algum momento. Às vezes, o código simplesmente não se encaixa nesse modelo e as contorções que se passam para tentar mantê-lo dessa maneira simplesmente não fazem sentido ... e, finalmente, tornam mais difícil o design.Às vezes, aceite que os ideais do design não podem ser realizados da maneira que você deseja no código. Diretrizes são diretrizes - não grilhões.
fonte
surname
ouname
ser nulo for um estado válido para o objeto, trate-o de acordo. Mas se não for um estado válido - fazendo com que os métodos dentro da classe gerem exceções, deve-se evitar que ele esteja nesse estado a partir do ponto em que foi inicializado. Você pode passar objetos incompletos, mas passar objetos inválidos é problemático. Se um sobrenome nulo for excepcional, tente evitá-lo mais cedo ou mais tarde.Eu não sou um cara de Java, mas isso parece aderir às duas restrições que você apresentou.
getCompleteName
não gera uma exceção se os nomes não forem inicializados edisplayCompleteNameOnInvoice
gera.fonte
getCompleteName()
não precisa se preocupar se a propriedade está realmente armazenada ou se é computada em tempo real.getCompleteName
, o que é hediondo.getCompleteName()
no restante da classe ou mesmo em outras partes do programa. Em alguns casos, o retornonull
pode ser exatamente o que é necessário. Porém, embora exista UM método cuja especificação seja "lance uma exceção neste caso", é EXATAMENTE o que devemos codificar.Parece que ninguém está respondendo sua pergunta.
Ao contrário do que as pessoas gostam de acreditar, "evitar objetos inválidos" não costuma ser uma solução prática.
A resposta é:
Sim deveria.
Exemplo fácil:
FileStream.Length
em C # /. NET , que lançaNotSupportedException
se o arquivo não é procurável.fonte
Você tem todo o nome do nome de cabeça para baixo.
getCompleteName()
não precisa saber quais funções o utilizarão. Portanto, não ter umname
quando ligardisplayCompleteNameOnInvoice()
nãogetCompleteName()
é responsabilidade.Mas,
name
é um pré-requisito claro paradisplayCompleteNameOnInvoice()
. Portanto, deve assumir a responsabilidade de verificar o estado (ou delegar a responsabilidade de verificar para outro método, se você preferir).fonte