No design do meu programa, geralmente chego ao ponto em que tenho que passar instâncias de objetos por várias classes. Por exemplo, se eu tiver um controlador que carrega um arquivo de áudio e o passa para um player, e o player para o playerRunnable, que o transmite novamente em outro lugar etc. Parece meio ruim, mas eu não sabe como evitá-lo. Ou está tudo bem 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.
fonte
Interessante que ninguém tenha falado sobre objetos imutáveis ainda. Eu argumentaria que passar um objeto imutável por todas as várias camadas é realmente uma coisa boa , em vez de criar muitos objetos de vida curta para cada camada.
Há algumas ótimas discussões sobre imutabilidade por Eric Lippert em seu blog
Por outro lado, eu argumentaria que a passagem de objetos mutáveis entre as camadas é um design ruim . Você está essencialmente construindo uma camada com a promessa de que as camadas adjacentes não a alterarão de maneira a quebrar seu código.
fonte
Passar instâncias de objetos é uma coisa normal a se fazer. Reduz a necessidade de manter o estado (ou seja, variáveis de instância) e desacopla o código de seu contexto de execução.
Um problema que você pode enfrentar é a refatoração, quando você deve alterar as assinaturas de vários métodos ao longo da cadeia de chamadas em resposta à alteração dos requisitos de parâmetros de um método próximo à parte inferior dessa cadeia. No entanto, isso pode ser mitigado com o uso de modernas ferramentas de desenvolvimento de software que ajudam na refatoração.
fonte
Talvez menor, mas existe o risco de atribuir essa referência em algum lugar de uma das camadas, potencialmente causando uma referência pendente ou vazamento de memória posteriormente.
fonte
Se você estiver passando objetos simplesmente porque é necessário em uma área remota do seu código, o uso da inversão dos padrões de design de controle e injeção de dependência, juntamente com, opcionalmente, um contêiner IoC apropriado pode resolver problemas de transporte de instâncias de objetos. Eu o usei em um projeto de tamanho médio e nunca mais consideraria escrever um grande pedaço de código do servidor sem usá-lo.
fonte
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ê está passando seus dados para vários objetos na mesma camada para atingir seu objetivo.
fonte
Resposta rápida: Não há nada errado em passar instâncias de objetos. Como também mencionado, o ponto é pular a atribuição dessa 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 uma vez mais complexos uma vez, como para informações resumidas.
fonte
Sou principalmente um desenvolvedor de interface do usuário da Web, mas me parece que seu desconforto intuitivo pode ser menos sobre a passagem da instância e mais sobre o fato de você estar um pouco processual com esse controlador. Seu controlador deveria suar todos esses detalhes? Por que ele faz referência a mais do que o nome de outro objeto para reproduzir o áudio?
No design de POO, costumo pensar em termos do que é sempre-verde e do que é mais provável que esteja sujeito a alterações. O assunto para mudar as 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ê deseja 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 / sempre-verde de reproduzi-lo. O material do reprodutor 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 embaixo da interface de um objeto composto maior, o IMO, e você não precisa reescrever seu controlador quando os detalhes de como o áudio é reproduzido mudam. Então, quando você passa uma instância de objeto com os detalhes, como a localização do arquivo, para o objeto maior, toda a troca é feita no interior de um contexto apropriado, onde é menos provável que alguém faça algo bobo com ela.
Portanto, neste caso, não acho que seja a instância do objeto sendo lançada que possa estar incomodando você. É que o capitão Picard está correndo para a sala de máquinas para ligar o núcleo da urdidura, correndo de volta para a ponte para traçar as coordenadas e pressionando o botão "socar" depois de ligar os escudos em vez de simplesmente dizer "Pegue nós ao planeta X na Warp 9. Faça isso. " e deixando sua equipe resolver os detalhes. Porque quando ele lida com isso dessa maneira, ele pode comandar qualquer navio da frota sem conhecer o layout de cada navio e como tudo funciona. E essa é, em última análise, a maior vitória em design de OOP a ser alcançada, a IMO.
fonte
É 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.
fonte
Esse problema pode ser resolvido com variáveis com escopo dinâmico, se o seu idioma as possuir, ou armazenamento local por thread. Esses mecanismos permitem associar algumas variáveis personalizadas a uma cadeia de ativação ou segmento de controle, para que não tenhamos que passar esses valores para um código que não tem nada a ver com eles, apenas para que eles possam ser comunicados a algum outro código o que precisa deles.
fonte
Como as outras respostas apontaram, esse não é um design inerentemente ruim. Ele pode criar um acoplamento rígido entre as classes aninhadas e as 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 ao 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 através de objetos aninhados, você poderia manter na classe da controladora referências a todos os objetos aninhados.
Como exatamente isso é implementado (ou se é uma solução válida) depende do design atual do sistema, como:
Esse é um problema que encontrei em um padrão de design MVC para um cliente GXT. Nossos componentes da GUI continham componentes da GUI aninhados para várias camadas. Quando os dados do modelo foram atualizados, acabamos passando-o pelas várias camadas até atingir o (s) componente (s) apropriado (s). Ele criou acoplamentos indesejados entre os componentes da GUI porque, se quiséssemos que uma nova classe de componente da GUI aceitasse dados do modelo, teríamos que criar métodos para atualizar os dados do modelo em todos os componentes da GUI que continham a nova classe.
Para corrigi-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, a View pudesse enviar os dados do modelo atualizados diretamente para os componentes da GUI necessários, final da história . Isso funcionou bem porque havia apenas instâncias únicas de cada componente da GUI. Pude 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.
fonte
O que você está descrevendo é chamado de padrão de design da Cadeia de Responsabilidade . A Apple usa esse padrão para o sistema de manipulação de eventos, pelo que vale a pena.
fonte