Como implementar o isValid corretamente?

5

Estou tentando fornecer um mecanismo para validar meu objeto assim:

class SomeObject {
    private $_inputString;
    private $_errors=array();

    public function __construct($inputString) {
        $this->_inputString = $inputString;      
    }

    public function getErrors() {
        return $this->_errors;
    }

    public function isValid() {
        $isValid = preg_match("/Some regular expression here/", $this->_inputString);
        if($isValid==0){
            $this->_errors[]=  'Error was found in the input';
        }
        return $isValid==1;
    }

}

Então, quando estou testando meu código, estou fazendo assim:

    $obj = new SomeObject('an INVALID input string');

    $isValid = $obj->isValid();
    $errors=$obj->getErrors();

    $this->assertFalse($isValid);
    $this->assertNotEmpty($errors);

Agora o teste passa corretamente, mas notei um problema de design aqui. E se o usuário ligasse $obj->getErrors() antes de chamar $obj->isValid() ?

O teste falhará porque o usuário deve validar o objeto antes de verificar o erro resultante da validação. Acho que dessa forma o usuário depende de uma sequência de ações para funcionar corretamente, o que acho ruim, porque expõe o comportamento interno da classe.

Como posso resolver este problema?
Devo dizer ao usuário explicitamente para validar primeiro? Onde eu menciono isso?
Devo mudar a maneira como eu valido? Existe uma solução melhor para isso?

UPDATE:
Eu ainda estou desenvolvendo a classe, então as mudanças são fáceis e renomear funções e refatorá-las é possível.

    
por Songo 11.10.2012 / 21:59
fonte

4 respostas

2

Existem duas maneiras de contornar este problema:

  1. Para definir $_errors como um erro indicando que a validação ainda não aconteceu ou
  2. Para "completamente sem estado": faça isValid retornar a matriz de erros e verifique o tamanho da matriz para determinar se a validação foi bem-sucedida ou não

O primeiro caminho é mais fácil para um sistema existente: tornar essa mudança muito mais barata, mas a interação com o objeto permanece no estado, no sentido de que depende da ordem das chamadas.

O segundo caminho pode ser mais difícil de integrar, mas se for aplicado de forma consistente, facilita a aprendizagem de sua API. Diferentes idiomas fornecem maneiras diferentes de abordar a necessidade de retornar vários itens de uma chamada. Em linguagens que permitem passagem por referência ou por ponteiro, a função de validação geralmente retorna um valor booleano, e um espaço reservado para erros é passado por referência no lado.

    
por 11.10.2012 / 22:14
fonte
5

Por que você permitiria que um consumidor de sua classe criasse um objeto em um estado inválido? Eu projetaria a classe de forma que, se alguma vez for solicitado a inserir um estado inválido (seja por parâmetros de construtor inválido ou solicitando uma mutação inválida), ele lança uma exceção e não altera o estado.

    
por 11.10.2012 / 23:29
fonte
0

Faça isValid equal algo por padrão que só é alterado quando está marcado ... Ao chamar getErrors , verifique o valor padrão e retorne uma exceção, se estiver lá ... Dessa forma, getErrors não funcionará a menos que isValid tenha sido executado primeiro.

    
por 11.10.2012 / 22:13
fonte
0

Torne $isValid uma variável de instância.

Faça a verificação de erros no construtor, incluindo o preenchimento da matriz.

A função isValid() só retorna $isValid .

Fazendo a verificação de erros no tempo do construtor - desacopla getErrors() de isValid()

    
por 12.10.2012 / 02:12
fonte