Temos um aplicativo da Web em que temos muitos (> 50) de pequenos componentes da Web que interagem entre si.
Para manter tudo dissociado, temos como regra que nenhum componente pode fazer referência direta a outro. Em vez disso, os componentes disparam eventos que são então (no aplicativo "principal") conectados para chamar os métodos de outro componente.
Com o passar do tempo, mais e mais componentes foram adicionados e o arquivo do aplicativo "principal" ficou cheio de pedaços de código parecidos com o seguinte:
buttonsToolbar.addEventListener('request-toggle-contact-form-modal', () => {
contactForm.toggle()
})
buttonsToolbar.addEventListener('request-toggle-bug-reporter-modal', () => {
bugReporter.toggle()
})
// ... etc
Para melhorar isso, agrupamos funcionalidades semelhantes, em Class
, dê um nome relevante, passe os elementos participantes ao instanciar e manipule a "fiação" dentro do Class
, da seguinte forma:
class Contact {
constructor(contactForm, bugReporter, buttonsToolbar) {
this.contactForm = contactForm
this.bugReporterForm = bugReporterForm
this.buttonsToolbar = buttonsToolbar
this.buttonsToolbar
.addEventListener('request-toggle-contact-form-modal', () => {
this.toggleContactForm()
})
this.buttonsToolbar
.addEventListener('request-toggle-bug-reporter-modal', () => {
this.toggleBugReporterForm()
})
}
toggleContactForm() {
this.contactForm.toggle()
}
toggleBugReporterForm() {
this.bugReporterForm.toggle()
}
}
e instanciamos assim:
<html>
<contact-form></contact-form>
<bug-reporter></bug-reporter>
<script>
const contact = new Contact(
document.querySelector('contact-form'),
document.querySelector('bug-form')
)
</script>
</html>
Estou muito cansado de introduzir padrões próprios, especialmente aqueles que não são realmente OOP-y, pois estou usando Classes
como meros recipientes de inicialização, por falta de uma palavra melhor.
Existe um padrão definido melhor / mais conhecido para lidar com esse tipo de tarefa que estou perdendo?
fonte
Respostas:
O código que você tem é muito bom. O que parece um pouco desanimador é que o código de inicialização não faz parte do próprio objeto. Ou seja, você pode instanciar um objeto, mas se você esquecer de chamar sua classe de fiação, é inútil.
Considere um Centro de Notificação (também conhecido como Barramento de Eventos) definido algo como isto:
Este é um manipulador de eventos de envio múltiplo DIY. Você seria capaz de fazer sua própria fiação simplesmente exigindo um NotificationCenter como argumento do construtor. O envio de mensagens para ele e a espera de passar as cargas úteis é o único contato que você tem com o sistema, por isso é muito SÓLIDO.
Nota: usei literais de string no local para que as chaves sejam consistentes com o estilo usado na pergunta e para simplificar. Isso não é aconselhável devido ao risco de erros de digitação. Em vez disso, considere usar uma enumeração ou constantes de seqüência de caracteres.
No código acima, a Barra de Ferramentas é responsável por informar ao NotificationCenter em que tipo de eventos está interessado e publicar todas as suas interações externas por meio do método de notificação. Qualquer outra classe interessada no
toolbar-button-click-event
simplesmente se registraria no construtor.Variações interessantes sobre esse padrão incluem:
Recursos interessantes incluem:
Algumas dicas interessantes e possíveis soluções incluem:
fonte
buttonsToolbar
para outro projeto que não use um barramento de eventos?Eu costumava introduzir algum tipo de "barramento de eventos" e, nos anos seguintes, comecei a confiar cada vez mais no próprio Modelo de Objeto de Documento para comunicar eventos para o código da interface do usuário.
Em um navegador, o DOM é a única dependência que está sempre presente - mesmo durante o carregamento da página. A chave é utilizar eventos personalizados em JavaScript e confiar na borbulhar de eventos para comunicar esses eventos.
Antes de as pessoas começarem a gritar sobre "aguardar a conclusão do documento" antes de anexar assinantes, a
document.documentElement
propriedade faz referência ao<html>
elemento a partir do momento em que o JavaScript inicia a execução, independentemente de onde o script é importado ou da ordem em que aparece na sua marcação.É aqui que você pode começar a ouvir eventos.
É muito comum ter um componente JavaScript (ou widget) dentro de uma determinada tag HTML na página. O elemento "raiz" do componente é onde você pode acionar seus eventos de bolhas. Os assinantes do
<html>
elemento receberão essas notificações como qualquer outro evento gerado pelo usuário.Apenas um exemplo de código da placa da caldeira:
Então o padrão se torna:
document.documentElement
propriedade (não é necessário aguardar o documento estar pronto)document.documentElement
.Isso deve funcionar para bases de código funcionais e orientadas a objetos.
fonte
Eu uso esse mesmo estilo no meu desenvolvimento de videogame com o Unity 3D. Crio componentes como Saúde, Entrada, Estatísticas, Som, etc. e os adiciono a um Objeto de jogo para criar o que é esse objeto de jogo. O Unity já tem mecânica para adicionar componentes aos objetos do jogo. No entanto, o que eu descobri foi que quase todo mundo estava consultando componentes ou fazendo referência direta a componentes dentro de outros componentes (mesmo que eles usassem interfaces, ainda é mais acoplado, obrigado pela preferência). Eu queria que os componentes pudessem ser criados isoladamente com zero dependências de quaisquer outros componentes. Então, eu tive os componentes disparando eventos quando os dados foram alterados (específicos ao componente) e declarando métodos para alterar basicamente os dados. Em seguida, o objeto do jogo para o qual eu criei uma classe e colei todos os eventos de componentes em outros métodos de componentes.
O que eu mais gosto nisso é que, para ver todas as interações dos componentes de um objeto de jogo, posso apenas olhar para essa classe 1. Parece que sua classe Contact é muito parecida com as minhas classes Game Object (eu nomeio objetos de jogo para o objeto que eles devem ser como MainPlayer, Orc, etc).
Essas classes são uma espécie de classes de gerente. Eles mesmos realmente não têm nada, exceto instâncias de componentes e o código para conectá-los. Não sei por que você cria métodos aqui que chamam outros métodos de componentes quando você pode conectá-los diretamente. O objetivo desta aula é realmente apenas organizar o evento.
Como uma observação lateral para meus registros de eventos, adicionei um retorno de chamada de filtro e retorno de chamada de args. Quando o evento é acionado (eu criei minha própria classe de evento personalizada), ele chamará o retorno de chamada do filtro, se houver, e se retornar true, passará para o retorno de chamada args. O objetivo do retorno de chamada do filtro era dar flexibilidade. Um evento pode ser acionado por vários motivos, mas eu só quero chamar meu evento conectado se uma verificação for verdadeira. Um exemplo pode ser um componente de entrada com um evento OnKeyHit. Se eu tiver um componente Movimento que possui métodos como MoveForward () MoveBackward (), etc, eu posso conectar o OnKeyHit + = MoveForward, mas obviamente eu não gostaria de avançar com nenhum pressionamento de tecla. Eu só gostaria de fazer isso se a chave fosse 'w'. Como o OnKeyHit está preenchendo argumentos para passar adiante e um deles é a chave que foi atingida,
Para mim, a assinatura de uma classe específica de gerenciador de objetos de jogo se parece mais com:
Como os componentes podem ser desenvolvidos isoladamente, vários programadores poderiam tê-los codificado. Com o exemplo acima, o codificador de entrada deu ao objeto de argumento uma variável chamada Key. No entanto, o desenvolvedor do componente Movement pode não ter usado Key (se for necessário examinar os argumentos, nesse caso provavelmente não, mas em outros, eles usam os valores de argumento passados). Para remover esse requisito de comunicação, o retorno de chamada args atua como um mapeamento para os argumentos entre os componentes. Portanto, a pessoa que faz essa classe de gerenciador de objetos de jogo é aquela que precisa apenas conhecer os nomes das variáveis arg entre os 2 clientes quando eles os conectam e realizam o mapeamento nesse ponto. Este método é chamado após a função de filtro.
Portanto, na situação acima, a pessoa de Entrada nomeou uma variável dentro do objeto args 'Key', mas o Movimento o nomeou 'keyPressed'. Isso ajuda ainda mais o isolamento entre os componentes em si, à medida que eles estão sendo desenvolvidos, e coloca o implementador da classe gerente na conexão correta.
fonte
Pelo que vale, estou fazendo algo como parte de um projeto de back-end e adotei uma abordagem semelhante:
Como eu lidei com seus desafios de construção / fiação:
A analogia com sua barra de ferramentas:
Problemas que você pode enfrentar:
.filter
acordo com o estado (na verdade, a implementação é mais funcional - a conversa é um mapa plano de observáveis de um evento observável de mudança de estado).Portanto, em resumo, você pode considerar todo o domínio do problema como observável ou 'funcionalmente' / 'declarativamente' e considerar seus componentes da Web como fluxos de eventos, como observáveis, derivados do barramento (um observável), ao qual o barramento (um observador) também está inscrito. A instanciação de observáveis (por exemplo: uma nova barra de ferramentas) é declarativa, pois todo o processo pode ser visto como observável dos observáveis
.map
'd do fluxo de entrada.fonte