Como evito gravar muitas funções de passagem em um wrapper?


13

Eu tenho uma classe, que envolve outra classe de um tipo de base comum. Como a interface do tipo base é bastante grande, isso envolve escrever muitas funções de passagem. Estou procurando uma maneira de evitar isso.

Vamos fazer um exemplo:

      Car
     /   \
Volvo     VolvoWithTrailer

Agora eu tenho que implementar todas as funções na interface do carro para o VolvoWithTrailer e chamar a função apropriada no objeto Volvo envolvido, exceto talvez GetMaxSpeed ​​(), que retornará algo mais baixo. Basicamente, terei muitas funções como

int VolvoWithTrailer::GetNumSeats() {
  return mVolvo.GetNumSeats()
}

Uma maneira óbvia de resolver isso seria fazer da VolvoWithTrailer uma subclasse da Volvo

    Car
     |
   Volvo
     |
VolvoWithTrailer

mas isso parece violar o princípio de favorecer a composição sobre a herança.

De que outra forma eu poderia evitar escrever todos esses wrappers (a linguagem é C ++)? Ou - se essa é a sua posição - por que devo escrevê-las / usar apenas herança? Existe alguma mágica de modelo que possa ajudar com isso?


2
Não pensei muito nisso e pensei em Java. Que tal uma interface de 'Trailer'? O VolvoWithTrailer estenderia o Volvo e implementaria a interface Trailer?

7
Favorecer a composição sobre a herança somente quando fizer sentido.
22713 Robert Harvey

@RobertHarvey: +1. Essa é uma diretriz, não um "princípio" e certamente não um mandamento absoluto.
Mason Wheeler

No Python, você pode resolver isso com introspecção (o que C # chama de reflexão).
usar o seguinte comando

1
Seu IDE não lida com a geração de código?
Amy Blankenship

Respostas:


5

Minha opinião é que você deve escrever o que precisar para fazer o estabilidade e a manutenção do programa longo prazo . Qual é o sentido de evitar escrever 20 linhas de código hoje, se toda vez que você toca em algo que usa esse código ou volta para manter essa Classe, isso custa mais 5 minutos ou mais porque você não investiu hoje?

Então a pergunta se torna "o que você precisa escrever?" Eu não acho que você fornecer informação suficiente para ser capaz de dar uma resposta definitiva, por isso vou listar algumas coisas que eu poderia pensar em fazer uma decisão:

1. Você precisa ter o objeto Trailer sozinho, agora ou em um futuro previsível?
Nesse caso, você tem um argumento bastante bom para criar o wrapper em torno de ambos os objetos compostos, pois você já precisará envolver um ou outro. Pode muito bem ser consistente.

2. Existe um dos três tipos (Car, Trailer, CarWithTrailer) em uma área do código em que os desenvolvedores acessam com frequência ou que parecem se tornar assim?
Nesse caso, tenha muito cuidado, pois as ramificações da escolha da coisa errada podem ser muito amortizadas sempre que o código for tocado. Caso contrário, pode não fazer qualquer diferença o que você decidir. Basta escolher uma direção e seguir em frente.

3. O que é mais fácil de entender?
Será que uma abordagem se repete quando alguém que está atrás de você "recebe" imediatamente o que você está tentando fazer? Seus colegas de equipe têm tendências específicas que podem dificultar uma abordagem para sua manutenção? Se tudo for igual, escolha a solução que impõe a menor carga cognitiva.

4. Há vantagens adicionais em usar uma abordagem em detrimento de outra?
Uma coisa que me impressiona é que a idéia do invólucro tem uma vantagem distinta se você escrever que o CarWithTrailerWrapper pode envolver qualquer carro e qualquer trailer em um CarWithTrailer. Então, você acaba escrevendo todos os seus métodos de passagem, mas apenas os escreve uma vez , em vez de uma vez para cada classe de carro.

Isso torna seu investimento inicial na escrita dos métodos de passagem muito mais baratos à medida que você adiciona mais carros e reboques. Também ajuda a reduzir a tentação de acoplar as coisas diretamente ao Volvo ou VolvoWithTrailer, enquanto reduz as conseqüências do acoplamento ao CarWithTrailerWrapper, pois você pode fazer muito mais com apenas essa implementação.

Talvez haja vantagens distintas que você obteria de graça usando o método de extensão, mas eu não as vejo.

OK, parece que eu me convenci a seguir em frente e a preencher os métodos de passagem, para aplicativos não triviais.

Como não sou programador de C ++, não tenho idéia de quais ferramentas de geração de código estão disponíveis para você. O IDE que eu uso pode gerar métodos a partir de uma Interface, e eu posso adicionar modelos de código que tornariam as passagens de escrita bastante simples. Se você não tiver acesso a um IDE que faça isso, poderá realmente ir muito longe, deixando o Excel escrever código para você .


3

Como a interface do tipo base é bastante grande, isso envolve escrever muitas funções de passagem.

E aí está o seu problema.

As interfaces devem lidar apenas com uma responsabilidade. Mantê-los finos e focados limita a quantidade de trabalho de passagem que você pode precisar no caso de um decorador, proxy ou outro invólucro (além de tornar a classe mais simples de usar, testar, manter e estender).


5
Uh o quê? Uma classe pode implementar várias interfaces, e as interfaces podem herdar uma da outra. Portanto, mesmo que uma interface seja muito pequena, isso não tem nada a ver com as Classes que as implementam ou não precisarão de muitas funções ou funções de passagem. O menor e mais bem definida as aulas , o mais provável se torna que você vai precisa de pass-through funções.
Amy Blankenship
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.