É uma prática ruim passar instâncias através de várias camadas?

59

No design do meu programa, muitas vezes chego ao ponto em que tenho que passar instâncias de objetos por várias classes. Por exemplo, se eu tenho um controlador que carrega um arquivo de áudio, e depois passa para um jogador, e o jogador passa para o playerRunnable, que passa novamente em outro lugar, etc. Parece meio ruim, mas eu não sabe como evitá-lo. Ou é OK fazer isso?

EDIT: Talvez o exemplo do player não seja o melhor, porque eu poderia carregar o arquivo mais tarde, mas em outros casos isso não funciona.

    
por Puckl 27.09.2012 / 14:32
fonte

12 respostas

54

Como outros já mencionaram, isso não é necessariamente uma prática ruim, mas você deve prestar atenção em não separar as camadas de interesses e passar instâncias específicas de camadas entre camadas. Por exemplo:

  • Objetos de banco de dados nunca devem ser passados para camadas superiores. Eu vi programas usando a classe DataAdapter do .NET, um DB- classe de acesso, e passando-o para a camada de interface do usuário, em vez de usar o DataAdapter no DAL, criando um DTO ou dataset, e passando isso para cima. O acesso ao banco de dados é o domínio do DAL.
  • Objetos de UI devem, é claro, ser limitados à camada de interface do usuário. Novamente, vi isso violado, tanto com ListBoxes preenchidos com dados do usuário transmitidos para a camada BL, em vez de um array / DTO de seu conteúdo, e (um favorito em particular meu), uma classe DAL que recuperava dados hierárquicos de o banco de dados, e em vez de retornar uma estrutura hierárquica de dados, ele apenas criou e preencheu um objeto TreeView e o retornou à interface do usuário para ser adicionado dinamicamente a um formulário.

No entanto, se as instâncias que você está passando forem as próprias DTOs ou entidades, provavelmente está tudo bem.

    
por 27.09.2012 / 15:10
fonte
15

Interessante que ninguém falou sobre Objetos imutáveis ainda. Eu diria que passar um objeto imutável ao redor através de todas as várias camadas é, na verdade, uma coisa boa , em vez de criar muitos objetos de vida curta para cada camada.

Existem algumas grandes discussões sobre a imutabilidade por Eric Lippert no blog dele

Por outro lado, eu diria que passar objetos mutáveis entre camadas é um design ruim . Você está essencialmente criando uma camada com a promessa de que as camadas adjacentes não a alterarão de maneira a quebrar seu código.

    
por 27.09.2012 / 18:12
fonte
13

Passar instâncias de objeto é algo normal. Isso reduz a necessidade de manter o estado (ou seja, variáveis de instância) e separa o código de seu contexto de execução.

Um problema que você pode enfrentar é a refatoração, quando é necessário alterar as assinaturas de vários métodos ao longo da cadeia de invocação, em resposta à alteração dos requisitos de parâmetro de um método próximo à parte inferior dessa cadeia. No entanto, ele pode ser mitigado com o uso de ferramentas modernas de desenvolvimento de software que ajudam na refatoração.

    
por 27.09.2012 / 14:49
fonte
8

Talvez seja menor, mas há o risco de atribuir essa referência em algum lugar em uma das camadas que possivelmente causam uma referência pendente ou vazamento de memória posteriormente.

    
por 27.09.2012 / 14:59
fonte
7

Se você está passando objetos simplesmente porque é necessário em uma área remota do seu código, usando a inversão de controle e os padrões de design de injeção de dependência, juntamente com, opcionalmente, um contêiner IoC apropriado, podem resolver problemas relacionados à execução de instâncias de objetos. Eu usei isso em um projeto de tamanho médio e nunca mais consideraria escrever um grande código de servidor sem usá-lo.

    
por 27.09.2012 / 19:28
fonte
4

Passar dados através de várias camadas não é uma coisa ruim, é realmente a única maneira que um sistema em camadas pode funcionar sem violar a estrutura em camadas. O sinal de que há problemas é quando você passa seus dados para vários objetos na mesma camada para atingir sua meta.

    
por 27.09.2012 / 14:48
fonte
3

Resposta rápida: Não há nada de errado ao passar por instâncias de objetos. Como também mencionado, o ponto é ignorar a atribuição desta referência em todas as camadas, potencialmente causando uma referência pendente ou vazamentos de memória.

Em nossos projetos, usamos essa prática para passar DTOs (objeto de transferência de dados) entre camadas e é uma prática muito útil. Também reutilizamos nossos objetos dto para construir mais complexos uma vez, como para informações resumidas.

    
por 27.09.2012 / 15:27
fonte
3

Sou principalmente um desenvolvedor de interface do usuário da web, mas parece-me que seu desconforto intuitivo pode ser menos sobre a passagem da instância e mais sobre o fato de que você está sendo um pouco processual com esse controlador. Seu controlador deve estar suando todos esses detalhes? Por que ele faz referência a mais do que o nome de outro objeto para reproduzir áudio?

No design da POO, tenho a tendência de pensar em termos do que é sempre verde e do que é mais provável que esteja sujeito a alterações. O assunto para mudar coisas é o que você vai querer colocar em suas caixas de objetos maiores para que você possa manter interfaces consistentes mesmo quando os jogadores mudam ou novas opções são adicionadas. Ou você está querendo trocar objetos ou componentes de áudio por atacado.

Nesse caso, seu controlador precisa identificar que é necessário reproduzir um arquivo de áudio e, em seguida, ter uma maneira consistente / persistente de reproduzi-lo. O material do player de áudio, por outro lado, pode mudar facilmente conforme a tecnologia e as plataformas são alteradas ou novas opções são adicionadas. Todos esses detalhes devem ficar sob uma interface de um objeto composto maior, IMO, e você não deveria ter que reescrever seu controlador quando os detalhes de como o áudio é reproduzido mudarem. Então, quando você passa uma instância de objeto com os detalhes como o local do arquivo para o objeto maior, toda essa troca é feita no interior de um contexto apropriado, onde é menos provável que alguém faça algo bobo com ele.

Portanto, neste caso, não acho que seja essa instância de objeto sendo lançada que pode estar incomodando você. É que o capitão Picard está correndo para a sala de máquinas para ligar o núcleo, correndo de volta até a ponte para traçar as coordenadas e, em seguida, apertar o botão "soco" depois de ligar os escudos ao invés de simplesmente dizer nós para o planeta X no Warp 9. Faça isso. " e deixar sua equipe resolver os detalhes. Porque quando ele lida dessa maneira, ele pode comandar qualquer navio da frota sem saber o layout de cada nave e como tudo funciona. E isso é, em última análise, o maior projeto de OOP para ganhar, IMO.

    
por 28.09.2012 / 03:00
fonte
2

É um design bastante comum que acaba acontecendo, embora você possa ter problemas de latência se o aplicativo for sensível a esse tipo de coisa.

    
por 27.09.2012 / 14:53
fonte
2

Esse problema pode ser resolvido com variáveis de escopo dinâmico, se a sua linguagem as tiver, ou armazenamento local de thread. Esses mecanismos nos permitem associar algumas variáveis customizadas a uma cadeia de ativação ou thread de controle, para que não tenhamos que passar esses valores para um código que não tenha nada a ver com eles apenas para que possam ser comunicados a algum outro código que precisa deles.

    
por 27.09.2012 / 19:48
fonte
2

Como as outras respostas apontaram, este não é um design inerentemente ruim. Ele pode criar um acoplamento rígido entre as classes aninhadas e aquelas que os aninham, mas afrouxar o acoplamento pode não ser uma opção válida se o aninhamento das referências fornecer um valor para o design.

Uma solução possível é "achatar" as referências aninhadas na classe do controlador.

Em vez de passar um parâmetro várias vezes por meio de objetos aninhados, você poderia manter nas referências da classe do controlador todos os objetos aninhados.

Como exatamente isso é implementado (ou se é mesmo uma solução válida) depende do design atual do sistema, como:

  • Você consegue manter algum tipo de mapa dos objetos aninhados no controlador sem que ele fique muito complicado?
  • Quando você passa o parâmetro para o objeto aninhado apropriado, o objeto aninhado pode reconhecer o parâmetro imediatamente ou há alguma funcionalidade adicional ocorrendo durante a passagem pelos objetos aninhados?
  • etc.

Este é um problema que encontrei em um padrão de design MVC para um cliente GXT. Nossos componentes da GUI continham componentes GUI aninhados para várias camadas. Quando os dados do modelo foram atualizados, acabamos passando-os pelas diversas camadas até chegar ao (s) componente (s) apropriado (s). Ele criou um acoplamento indesejado entre os componentes da GUI, porque se quiséssemos que uma nova classe de componente GUI aceitasse dados de modelo, tínhamos que criar métodos para atualizar os dados do modelo em todos os componentes da GUI que continham a nova classe.

Para consertá-lo, mantivemos na classe View um mapa de referências a todos os componentes da GUI aninhados para que, sempre que os dados do modelo fossem atualizados, o View pudesse enviar os dados do modelo atualizados diretamente para os componentes da GUI que precisassem deles. fim da história. Isso funcionou bem porque havia apenas instâncias únicas de cada componente GUI. Eu podia ver que não estava funcionando tão bem se houvesse várias instâncias de alguns componentes da GUI, dificultando a identificação de qual cópia precisava ser atualizada.

    
por 08.10.2012 / 03:10
fonte
0

O que você está descrevendo é um padrão de design chamado Chain Of Responsibility .  A Apple usa esse padrão para seu sistema de manipulação de eventos, pelo que vale a pena.

    
por 27.09.2012 / 23:16
fonte