Introdução
Robert L. Glass argumentou que a prática vem antes da teoria. O desenvolvimento de software, em particular, é um campo onde a prática lidera a teoria, e os desenvolvedores aprendem mais quando estão envolvidos na criação de código concreto, 'sujando as mãos'. Portanto, vamos deixar a teoria e os conceitos de lado por enquanto e dar uma olhada em um programa simples.
Implementando um aplicativo de venda de ingressos
- Estamos planejando um pequeno evento para promover um pequeno teatro.
- Conteúdo do evento: enviar convites para assistir a uma apresentação gratuitamente para espectadores selecionados por sorteio.
- Precisamos separar os espectadores que ganharam o evento daqueles que não ganharam.
- Espectadores que ganharam o evento: trocar o convite por um ingresso.
- Espectadores que não ganharam o evento: comprar o ingresso com dinheiro.
Código de prática: https://github.com/Java-Crew/objects-study/tree/jayon/src/main/java/chapter01/step01
Qual é o problema?
Robert Martin descreve três funções que um módulo de software (independentemente do tamanho, como uma classe, pacote ou biblioteca, qualquer elemento que compõe um programa) deve ter.
- Funciona corretamente durante a execução.
- Existe para ser modificado.
- Deve ser possível fazer alterações com um mínimo de esforço.
- Se comunica com quem lê o código.
- Deve ser fácil para os desenvolvedores ler e entender.
O aplicativo de venda de ingressos anterior atende à primeira restrição, funcionando corretamente, mas não atende aos objetivos de facilidade de modificação e comunicação.
Código que foge às expectativas
Vamos analisar o motivo pelo qual ele não atende ao objetivo de comunicação.
- O que o método enter() da classe Theater faz?
- O teatro abre a bolsa do espectador para ver se há um convite dentro.
- Se houver um convite na bolsa, o teatro instrui o vendedor a transferir o ingresso armazenado na bilheteria para a bolsa do espectador.
- Se não houver um convite na bolsa, o teatro pega o dinheiro da bolsa do espectador, compra um ingresso e o coloca na bolsa.
- Do ponto de vista do espectador, ele precisa assistir a um terceiro (o teatro) mexendo em sua bolsa, pegando seu dinheiro e colocando um ingresso.
- Do ponto de vista do vendedor, ele precisa assistir a um terceiro (o teatro) manipulando os ingressos e dinheiro na bilheteria sem permissão.
- Código compreensível significa código que não diverge muito do que esperamos, e o teatro acima diverge do que esperamos.
- O espectador deve pegar o dinheiro de sua bolsa e pagar ao vendedor para receber o ingresso.
- O vendedor deve pegar o ingresso da bilheteria e entregá-lo ao espectador, recebendo o dinheiro do espectador e guardando-o na bilheteria.
- Além disso, para entender o método enter(), é necessário lembrar de vários detalhes.
- Audience possui Bag.
- Bag contém dinheiro e ingressos.
- TicketSeller vende ingressos em TicketOffice, e TicketOffice armazena dinheiro e ingressos.
Código vulnerável a alterações
O método enter() assume duas condições.
- O espectador sempre carrega uma bolsa para guardar dinheiro e convites.
- O vendedor vende ingressos apenas na bilheteria.
E se as seguintes situações acontecerem?
- O espectador pode não ter uma bolsa.
- O espectador pode usar um cartão de crédito em vez de dinheiro.
- O vendedor pode vender ingressos fora da bilheteria.
Por exemplo, para atender ao primeiro requisito, precisamos remover o objeto Bag de Audience e modificar o método enter() da classe Theater relacionado a ele. Isso ocorre porque a classe Theater depende muito do fato de que o espectador carrega uma bolsa e o vendedor vende ingressos apenas na bilheteria. Se qualquer um desses detalhes mudar, precisamos modificar a classe correspondente e as classes dependentes (por exemplo, Theater).
Este é um problema relacionado à dependência entre objetos, e a dependência implica impacto na alteração. No entanto, o design orientado a objetos tem como objetivo construir uma comunidade de objetos que cooperam enquanto dependem uns dos outros, então, em vez de remover a dependência sem pensar, precisamos manter apenas a dependência mínima necessáriae remover dependências desnecessárias.
Quando a dependência entre objetos é muito alta, dizemos que o acoplamentoé alto, e quanto maior o acoplamento entre dois objetos, maior a probabilidade de eles mudarem juntos. Portanto, o objetivo do design é reduzir o acoplamento entre objetos para criar um design que seja fácil de modificar.
Melhorando o design
O Theater não precisa saber que o espectador tem uma bolsa ou que o vendedor vende ingressos na bilheteria. O Theater simplesmente quer que o espectador entre no teatro. Portanto, precisamos tornar o espectador e o vendedor entidades autônomas, onde o espectador gerencia seu próprio dinheiro e convite, e o vendedor gerencia seus próprios ingressos e preços de venda na bilheteria.
Aumentar a autonomia
Um objeto que realiza apenas tarefas estreitamente relacionadas e delega tarefas não relacionadas a outros objetos é considerado altamente coeso. Criar objetos autônomos que gerenciam seus próprios dados reduz o acoplamento e aumenta a coesão.
Programação Procedural e Orientada a Objetos
- Programação Procedural
- O método enter() de Theater é um processo, e Audience, TicketSeller, Bag e TicketOffice são dados.
- A maneira como os processos e dados são colocados em módulos separados é chamada de programação procedural.
- Há muito código que vai contra nossa intuição. (por exemplo, o espectador gerencia seu próprio dinheiro e convite).
- É difícil restringir o impacto das alterações nos dados.
- A responsabilidade é gerenciada de forma centralizada. (Theater gerencia tudo)
- Programação Orientada a Objetos
- A maneira como os dados e processos são colocados no mesmo módulo é chamada de programação orientada a objetos.
- Podemos escrever código que corresponde à nossa intuição.
- O impacto das alterações nos dados pode ser efetivamente restringido por meio de encapsulamento.
- Cada objeto é responsável por si mesmo.
Código de prática: https://github.com/Java-Crew/objects-study/tree/jayon/src/main/java/chapter01/step02
Pode ser ainda melhor
- A classe Bag da classe Audience ainda é uma entidade passiva que é controlada pela classe Audience, portanto, precisamos tornar a classe Bag uma entidade autônoma.
- O TicketOffice da classe TicketSeller também é controlado pela classe TicketSeller. Precisamos tornar o TicketOffice uma entidade autônoma.
- No entanto, após a alteração, o TicketOffice teve um acoplamento adicional com Audience.
- Como no design, devemos considerar cuidadosamente os trade-offs. Neste caso, para reduzir o acoplamento com Audience, podemos concordar em tornar o TicketOffice uma entidade ligeiramente passiva.
Código de prática: https://github.com/Java-Crew/objects-study/tree/jayon/src/main/java/chapter01/step03
Isso é mentira!
- Na realidade, mesmo que algo seja passivo, quando entra no mundo orientado a objetos, ele se torna uma entidade ativa e autônoma.
- É útil usar a personificação e pensar em entidades passivas como se estivessem rindo, falando e brigando.
Design Orientado a Objetos
Por que o design é necessário?
- Design é sobre como organizar o código.
- Um bom design é aquele que atende totalmente às necessidades de hoje e permite acomodar mudanças futuras sem problemas.
Design Orientado a Objetos
- Código modificável é código fácil de entender.
- O paradigma orientado a objetos nos ajuda a escrever código da mesma forma que vemos o mundo.
- Os objetos são entidades autônomas que são responsáveis por seus próprios dados.
- Um bom design orientado a objetos é um design que gerencia adequadamente as dependências entre objetos que cooperam.
Fontes
- Objetos
Comentários0