É ruim criar classes cujo único objetivo é ser convertido em outra classe implicitamente?

10

Imagine uma situação em que estamos usando uma biblioteca que permite criar Circleobjetos, onde você pode especificar o raio e o centro do círculo para defini-lo. No entanto, por algum motivo, também é necessário um flavourparâmetro necessário . Agora, digamos que eu realmente precise usar Circleno meu próprio aplicativo, mas para os fins do meu aplicativo, posso definir o sabor Flavours.Cardboardsempre.

Para "resolver" isso, eu crio minha própria Circleclasse em um espaço para nome diferente, que apenas aceita radiuse centercomo parâmetros, mas possui um conversor implícito na Circleclasse da biblioteca externa que apenas cria um Circle(this.radius, this.center, Flavours.Cardboard)objeto. Então, em todo lugar que eu preciso do outro tipo Circle, deixo a conversão automática ocorrer.

Quais são as conseqüências da criação dessa classe? Existem soluções melhores? Faria alguma diferença se meu aplicativo fosse uma API construída sobre essa biblioteca externa, destinada ao uso por outros programadores?

user3002473
fonte
Isso parece uma variação do Padrão do Adaptador , embora pareça ter um valor duvidoso aqui (sendo apenas uma conveniência para evitar a configuração de um parâmetro).
Robert Harvey
5
Por que não criar uma MakeCircle função ?
User253751 28/05
1
@immibis Thay foi meu primeiro pensamento. Quando estou criando jogos, geralmente crio uma função ao longo das linhas em makePlayerque apenas aceita cabos para colocar o jogador, mas delega para um construtor muito mais complexo.
Carcigenicate

Respostas:

13

Embora nem sempre seja ruim, é muito raro que as conversões implícitas sejam sua melhor opção.

Existem problemas.

  1. Conversões implícitas não são muito legíveis.
  2. Como você está criando um novo objeto, quaisquer alterações no objeto original não serão vistas por quem você está convertendo, levando a erros.
  3. Se você estiver jogando o objeto fora, isso é lixo extra para limpar.
  4. Se houver um problema, isso acontecerá quando a conversão ocorrer, não quando a coisa for criada, tornando os bugs mais difíceis de rastrear.

Em geral, existem melhores soluções.

  1. Crie seu próprio invólucro fino que use a outra classe / estrutura internamente.
  2. Crie um método auxiliar que aceite os argumentos do construtor que você deseja e forneça o que foi corrigido, retornando o objeto real sem precisar especificar o argumento do qual você não se importa.
  3. Herde da classe de problema e forneça seu próprio construtor, melhor.
  4. Perceba que passar na discussão extra realmente não é grande coisa.

Pessoalmente, acho o número 2 o mais simples de implementar e o menos oneroso no design. Os outros podem ficar bem, dada a situação e o que mais você está tentando fazer com essas classes.

A conversão implícita é o último recurso, e só me parece realmente valer a pena quando tenho functors no estilo C ++ que estou tentando criar - objetos de estratégia que eu implicitamente converto para delegar tipos.

Telastyn
fonte
1

Dado o cenário que você está descrevendo, você pode pensar nisso em termos de aplicação parcial da função.

Um construtor é uma função (pelo menos em teoria; em C #, você pode criar uma "função de fábrica" ​​que chama o construtor):

Func<double, Point, Flavour, Circle> MakeCircle = (r, p, f) => new Circle(r, p, f);

para aplicação parcial, o seguinte será suficiente:

public static Func<T1, T2, R> Partial<T1, T2, T3, R>(this Func<T1, T2, T3, R> func, T3 t3)
   => (t1, t2) => func(t1, t2, t3);

Agora você pode obter seu construtor que requer apenas 2 parâmetros:

Func<double, Point, Circle> MakeCardboardCircle = Circle.Partial(Flavours.Cardboard)

Então agora você tem uma função de fábrica com os parâmetros desejados

Circle c = MakeCardboardCircle(1.0, new Point(0, 0)))

BTW, isso é claramente equivalente à opção 2 acima, apenas de uma perspectiva mais funcional.

la-yumba
fonte