Boa noite , venho submeter meu 5º resumo do livro.

55 á 64

Através de interfaces e composição, podemos criar desde um novo driver JDBC para um determinado banco de dados, ou flexibilizar trabalhos simples, como ordernar listas com Comparators implementando diferentes critérios de comparação.

Este princípio, de manter suas classes abertas para extensão sem a necessidade de alteração no código original, é chamado Open Closed Principle.

Evite herança, favoreça composição é um de dois princípios fundamentais de design do livro Design Patterns, com frequência referenciado na literatura.

A classe Calendar permite mudança no estado de suas instâncias e, portanto, diz-se que a classe é mutável.

 A classe String tem comportamento diferente.

 Todo método invocado em uma String, que parece modificá-la, sempre devolve uma nova Favoreça imutabilidade e simplicidade Casa do Código instância com a alteração requisitada, mas nunca altera a instância original.

Ao tornar uma classe imutável, uma ampla gama de problemas comuns desaparece. Simplicidade e previsibilidade Objetos imutáveis são muito mais simples de manipular que os mutáveis.

Objetos imutáveis não sofrem efeitos colaterais, pois têm comportamento previsível em relação ao seu estado.

Os próprios engenheiros da Sun admitiram alguns erros de design das APIs antigas, como a classe java.util.Calendar ser mutável.

Por isso, muitas vezes recorremos às APIs de terceiros, como a Joda Time, nas quais encontramos entidades de datas, horários e intervalos imutáveis.

Quando o objeto é mutável, para evitar que alguém o modifique, temos que tomar alguma precaução.

Uma solução, frequentemente usada com coleções através do Collections.unmodifiableList(List) e seus similares, é interfacear o objeto e embrulhá-lo de tal forma que os métodos expostos não possibilitem modificação.

 Em vez de passar a referência original adiante, passamos essa nova referência, cuja instância lança exceções em qualquer tentativa de modificação.

 Outra solução para objetos mutáveis é criar cópias defensivas do objeto.

 Em vez de devolver o objeto original que se deseja preservar, criamos uma cópia, por exemplo, através do método clone, e devolvemos esta cópia.

Tópicos de Orientação a Objetos não tem como alterar o objeto original, mas recebe uma cópia para uso próprio, que, se alterar, afetará apenas a si mesmo.

Objetos imutáveis são mais simples de se lidar.

Depois de sua criação, sempre saberemos seu valor e não precisamos tomar cuidados adicionais para preservá-lo.

 Objetos mutáveis, em contrapartida, com frequência necessitam de um cuidado adicional, cujo objetivo é evitar alterações inesperadas.

Otimizações de memória podemos tirar proveito da imutabilidade de outras formas.

 Como cada objeto representa apenas um estado, você não precisa mais do que uma instância para cada estado.

 A própria API da linguagem Java usa a imutabilidade como uma vantagem para fazer cache e reaproveitamento de instâncias.

 É o caso do pool de Strings da JVM, que faz com que Strings de mesmo conteúdo possam ser representadas por uma única instância compartilhada.

 O mesmo acontece em boa parte das implementações das classes wrapper como Integer.

Ao invocar Integer.valueOf(int), a referência a Integer devolvida pode ser fruto de um cache interno de objetos com números mais frequentemente solicitados.

Esse tipo de otimização só é possível com objetos imutáveis.

 É seguro compartilhar instâncias de Strings ou Integers com diferentes partes do programa, sem o risco de que uma alteração no objeto traga efeitos colaterais indesejados em outras partes do sistema.

E há mais possibilidades ainda para otimizações graças à imutabilidade. A classe String ainda se aproveita dessa característica para compartilhar seu array de char interno entre Strings diferentes.

Quando pedimos uma determinada substring, em vez de o Java criar um array de char com o pedaço em questão, ele devolve um novo objeto String, que internamente compartilha a referência para o mesmo array de char que a String original, e tem apenas os seus dois índices ajustados.

Essa otimização é uma implementação um pouco mais simples do design pattern flyweight, em que se propõe reaproveitar objetos com objetivo de diminuir o uso da memória.

 O próprio pool de Strings pode ser considerado um flyweight.

E poderíamos ir mais longe com o flyweight, implementando a concatenação de Strings apontando para diversas outras Strings internamente ao invés de copiar os dados para seu próprio array de char.

É válido também ressaltar o perigo de certas otimizações em alguns casos. Na otimização do substring, por exemplo, uma String pequena pode acabar segurando referência para um array de chars muito grande se ela foi originada a partir de uma String longa.

Isso impediria que o array maior fosse coletado, mesmo se não possuirmos referências para a String original.

Há também um excesso de memória consumida temporariamente.

Com imutabilidade, cada invocação de método cria uma nova cópia do objeto apenas com alguma alteração e, se isto estiver dentro de um laço, diversas cópias temporárias serão utilizadas, mas apenas o resultado final é guardado.

Desta forma, evitamos que duas escritas concorrentes se entrelacem e que leituras sejam capazes de acessar dados inconsistentes ou intermediários.

 A tarefa difícil é definir todas as regiões críticas e quais são mutuamente exclusivas; definir uma região muito grande gera perda de vazão (throughtput), enquanto uma de tamanho insuficiente implicará os mesmos problemas de não tê-las.

Em vez de recorrer aos recursos das linguagens, pensemos nesses objetos de forma diferente, evitando esse estado intermediário, não permitindo a mudança de valores.

 A vantagem é a thread-safety, pois, como não existem estados intermediários, não há como acessar nem modificar dados em momentos de inconsistência.

O estado inconsistente fica escondido na lógica de construção, e o novo estado só estará disponível quando terminar de ser construído, para então ter sua referência compartilhada por mais de uma thread.

As linguagens funcionais mais puras, como Erlang e Haskell, estão sendo utilizadas em ambientes de grande concorrência, dada sua característica de trabalhar quase que apenas com valores imutáveis.

 Perde-se o conceito de variável como o conhecemos, já que os valores não mudam; nascem e morrem com o mesmo estado.

Você é obrigado a programar apenas de maneira imutável, o que é uma enorme mudança de paradigma.

 Muitas dessas linguagens podem rodar sobre a JVM, como Scala, que, apesar de suportar mutabilidade, tem fortes raízes em programação funcional e recomenda o uso de imutabilidade. Cada invocação de método que cria uma situação nova, cria um estado novo, devolve a referência para um objeto novo.

Uma classe Periodo imutável, implementada com Calendars, precisará trabalhar com cópias defensivas em dois momentos.

Primeiro, quando receber algum Calendar como parâmetro e, depois, quando devolver algum Calendar que faça parte da composição do objeto.

 Se não copiarmos os objetos, é possível que algum código externo à classe Periodo altere os Calendars em questão, gerando inconsistência.

Cuidado com o modelo anêmico Casa do Código Aproveitamos aqui o fato de Calendar implementar Cloneable, caso contrário precisaríamos fazer a cópia manualmente, criando um objeto e alterando os atributos pertinentes um a um.

 Como nossa classe é imutável, se precisarmos calcular alguma informação que exija qualquer modificação, clonamos o objeto.

 E, com uma pequena modificação, podemos implementar o design pattern flyweight em nossa classe, compartilhando a instância do Calendar de início do período entre o objeto original e o novo, com uma semana adiada.

Para tanto, precisaríamos de um outro construtor privado para ser chamado no a dia Uma Semana que não fizesse o clone.

 Utilizar classes imutáveis traz um trabalho a mais junto com os diversos benefícios descritos. Cuidado com o modelo anêmico Um dos conceitos fundamentais da orientação a objetos é o de que você não deve expor seus detalhes de implementação.

 Encapsulando a implementação, podemos trocá-la com facilidade, já que não existe outro código dependendo desses detalhes, e o usuário só pode acessar seu objeto através do contrato definido pela sua interface pública.

 O método setSaldo é um bom exemplo disso, já que dificilmente o saldo em uma entidade Conta será simplesmente “substituído” por outro. Para alterar o saldo de uma conta, é necessária alguma operação que faça mais sentido para o domínio, como saques ou depósitos.

 Nunca crie um getter ou setter sem uma necessidade real; lembre-se de que precisamos que essa necessidade seja clara para criar qualquer método que colocamos em uma classe. Particularmente, os getters e setters são campeões quando falamos em métodos que acabam nunca sendo invocados e, além disso, grande parte dos utilizados poderia ser substituída por métodos de negócio.

 Essa prática foi incentivada nos primórdios do AWT, para o qual era recomendado criar getters e setters para serem invocados no preenchimento de cada campo visual da sua interface gráfica com o usuário, cunhando o termo JavaBean.

Os EJBs também contribuíram para esta prática, como será visto adiante.

Se for preciso, por exemplo, que uma taxa seja debitada toda vez que um depósito é realizado, será necessário percorrer todo o código e modificar Cuidado com o modelo anêmico Casa do Código essas diversas invocações.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *