Pergunta

Eu ouvi muitas vezes por aqui a partir de desenvolvimento de pessoas test driven que ter uma função de obter grandes quantidades de informação de forma implícita é uma coisa ruim. Eu posso ver eram isso seria ruim do ponto de vista de testes, mas não é por vezes necessário do ponto de vista de encapsulamento? A seguinte pergunta vem à mente:

Está usando aleatória e OrderBy um algoritmo bom Shuffle ?

Basicamente, alguém queria criar uma função em C # para embaralhar aleatoriamente um array. Várias pessoas lhe disse que o gerador de números aleatórios deve ser passado como um parâmetro. Esta parece ser uma violação flagrante de encapsulamento para mim, mesmo que faz o teste mais fácil. Não é o fato de que uma série baralhar algoritmo requer qualquer estado em tudo que não seja a matriz é baralhar um detalhe de implementação que o chamador não deve ter que se preocupam? não seria o lugar correto para obter essa informação ser implicitamente, possivelmente a partir de um singleton thread-local?

Foi útil?

Solução

Sim, isso quebra encapsulamento . Tal como acontece com a maioria das decisões de design de software, este é um trade-off entre duas forças opostas. Se você encapsular o RNG então você torná-lo difícil de mudar para um teste de unidade. Se você faz um parâmetro, então você se torna mais fácil para um usuário para mudar o RNG (e potencialmente errar).

A minha preferência pessoal é para torná-lo fácil de teste, em seguida, fornecer uma implementação padrão (um construtor padrão que cria a sua própria RNG, neste caso particular) e uma boa documentação para o usuário final. Adicionando um método com a assinatura

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)

que cria um Random usando a hora do sistema atual como sua semente iria cuidar da maioria dos casos normais de uso deste método. O método original

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)

pode ser usado para testar (em passe um objecto Random com uma semente conhecida) e também nos casos raros em que um utilizador decide que precisam de um RNG criptograficamente segura. A implementação de um parâmetro deve chamar esse método.

Outras dicas

Eu não acho que ele quebra o encapsulamento. O único estado na matriz são os dados em si - e "uma fonte de aleatoriedade" é essencialmente um serviço. Por que um array têm, naturalmente, uma fonte associada de aleatoriedade? Por que isso tem que ser um singleton? E sobre diferentes situações que têm necessidades diferentes - por exemplo, velocidade vs aleatoriedade criptograficamente segura? Há uma razão pela qual java.util.Random tem uma subclasse SecureRandom :) Talvez ele não importa se os resultados da reprodução aleatória são previsíveis com muito esforço e observação - ou talvez ele faz. Isso vai depender do contexto, e isso é informação que o algoritmo shuffle não deve se preocupar.

Uma vez que você começar a pensar nisso como um serviço, faz sentido que ele é passado como uma dependência.

Sim, você poderia obtê-lo a partir de um singleton thread-local (e na verdade eu estou indo ao blogue sobre exatamente que nos próximos dias), mas eu faria geralmente código lo para que o < em> chamador começa a tomar essa decisão.

Uma das vantagens da "aleatoriedade como um serviço" conceito é que ele faz para a repetibilidade - se você tem um teste que falhar, você pode passar um Random com uma semente específica e sei que você sempre vai ter o mesmo resultados, o que torna a depuração mais fácil.

É claro, há sempre a opção de fazer o opcional Random -. Usar um singleton thread-local como padrão se o chamador não fornecer seu próprio

Eu não acho que isso viola o encapsulamento.

Seu Exemplo

Eu diria que ser capaz de fornecer uma RNG é uma característica da classe. Eu, obviamente, fornecer um método que não requer isso, mas eu posso ver vezes em que pode ser útil para ser capaz de duplicar a randomização.

E se o shuffler série era parte de um jogo que usou o RNG para a geração de nível. Se um usuário queria salvar o nível e reproduzi-lo novamente mais tarde, pode ser mais eficiente para armazenar a semente RNG.

Geral Caso

classes simples que têm uma única tarefa como esta, normalmente, não precisa se preocupar em divulgar o seu funcionamento interno. O que eles encapsular é a lógica da tarefa, não os elementos exigidos por essa lógica.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top