Padrão Factory

A criação de objetos pode ser algo extremamente complicado tanto que há vários padrões de projetos que tentam resolver de forma elegante e padronizada alguns destes problemas. Estes padrões de projetos são chamados de padrões de criação. A linguagem Java utiliza alguns destes padrões em várias APIs, hoje vamos discutir um pouco do padrão Factory.

No Java 7 fomos introduzidos à um conjunto de APIs denominada NIO.2 que define interfaces e classes para manipulação de arquivos. Em especial, a versão introduziu a interface java.nio.file.Path que visa substituir a API java.io.File para manipulação de arquivos.

Quando trabalhamos com sistemas de arquivos, temos de manipular arquivos e diretórios, seja por caminhos absolutos ou relativos, mas também temos diversas particularidades entre um sistema de arquivos e outro. Por exemplo, no Linux o caminho é absoluto se ele começar com / mas no Windows isso não é verdade. No Windows, o caminho é absoluto se ele começar com uma referência à uma partição, algo como C:.

É por esta razão que a API criada nos disponibiliza uma interface ao invés de uma classe. A implementação irá variar de sistema de arquivos para sistema de arquivos. Sendo assim, Como podemos escrever um código que cria objeto cujo o tipo específico só poderá ser conhecido em tempo de execução?

Seria uma complexidade desnecessária deixar a responsabilidade para o desenvolvedor e para simplificar a criação de uma instância de java.io.file.Path foi criada uma fábrica de Path chamada java.io.file.Paths.

Path path1 = Paths.get("C:\\lucas");		
Path path2 = Paths.get("/home/lucas");

System.out.println(path1.isAbsolute());
System.out.println(path2.isAbsolute());

//Output no Linux:
//false
//true

Perceba que o resultado deste método vai depender do sistema de arquivos sob o qual a JVM está executando e toda a complexidade possivelmente envolvida na criação de cada objeto Path é encapsulada pela fábrica.Algumas características de fábricas no Java são a utilização de métodos estáticos. Alguns exemplos de fábricas são java.nio.file.FileSystems e java.util.concurrent.Executors.

Perceba que existe uma nomenclatura comum nestas classes que terminam com um s no final. Mas isso não é um padrão. Ainda no NIO.2 temos uma classe java.nio.file.Files que é considerada uma classe utilitária. Classes utilitárias também são compostas por métodos estáticos mas diferem das fábricas por não criar objetos mas por manipular ou criar novos objetos de instâncias existentes.

Path path = Paths.get("/home/lucas");
Files.exists(path);

//Output no Linux:
//true

No exemplo acima, utilizamos a classe Files para chamar o método estático exists que opera em instâncias de java.nio.file.Path para verificar se o arquivo/diretório existe no sistema de arquivos.

Considerações finais

Reconhecer os padrões de projetos utilizados na linguagem é uma ótima forma de melhorar suas atividades de codificação. O padrão Factory é utilizado desde APIS SE até EE. Neste technote discutimos um pouco sobre a aplicação do padrão Factory na API NIO.2 e apresentamos algumas as operações que você costumava fazer com java.io.File e que é possível realizar de forma mais simplificada com esta API.

Até mais!

Modularidade com Java 9 – Parte 2

No texto anterior foi apresentado a problemática da visibilidade e acessibilidade dos tipos e como elas afetam a modularização em vários níveis, desde da plataforma até bibliotecas e como o JDK 9 pretende resolver este problema por meio da forte encapsulação do sistema de módulos. Agora, vamos verificar como esta solução é aplicada aplicações no Java 9.

Módulo de banco de dados

Vamos imaginar, para fins didáticos, que estamos desejando criar um módulo com funções auxiliares de conectividade com banco de dados, algo semelhante ao projeto do Spring JDBC, sendo assim, vamos criar uma interface que utiliza como parâmetro uma instância de java.sql.ResultSet:

package br.com.esig.database;

import java.sql.ResultSet;

public interface MapeadorLinha<T> {
    T mapear(ResultSet res, int index);
}

Declaração de módulos

Os arquitetos da linguagem decidiram definir o módulo como um elemento da linguagem, semlhante ao que fazemos com classes e interfaces. O arquivo de declaração do módulo é, por convenção, definido em um arquivo chamado module-info.java e deve ficar na raiz dos pacotes. Os nomes dos módulos, com os pacotes, não devem conflitar. É recomendado a nomeclatura do módulo como sendo o nome reverso do domínio da organização. Um declaração de módulo simples seria como a seguinte:

// module-info.java
module br.com.esig.database {
}

Com isso, ao tentar compilar o nosso projeto encontramos a seguinte mensagem de erro:

// MapeadorLinha.java: error: package java.sql is not visible
// import java.sql.ResultSet;
//  (package java.sql is declared in module java.sql, but module 
// br.com.esig.database does not read it)
// 1 error

A mensagem de erro acima, apresentada no momento da compilação, mostra que a classe java.sql.ResultSet não é reconhecida pelo compilador! Isto ocorre pois, a partir do JDK 9, quando incluimos um arquivo module-info.java os únicos tipos acessíveis para utilização são exportados pelo módulo java.base, que contém tipos definidos nos pacote java.lang, java.util, java.nio, java.timee alguns outros.

Para que nosso código funcione, temos de definir quais módulos nossa aplicação terá acesso (visibilidade) e quais tipos nosso pacote irá permitir acesso (acessibilidade). Uma segunda versão da nossa definição de módulos seria a seguinte:

module br.com.esig.database {
    requires java.sql;
    exports br.com.esig.database.api; 
}

Um módulo é definido pela palavra chave module seguido do nome do módulo, nesse caso, br.com.esig.database. Logo em seguida, no corpo do módulo, solicitamos o acesso ao módulo java.sql por meio da palavra chave requiresjava.sql. Estamos dizendo que o módulo br.com.esig.database irá visualizar os tipos exportados pelo módulo java.sql ou, de forma equivalente, o módulo java.sql é acessível pelo br.com.esig.database.

Ao informar que nosso módulo requiresjava.sql estamos informando ao JDK que, desejamos possuir acesso à todas as classes, interfaces, recursos estáticos que são exportados pelo módulo. Com isso, conseguimos encontrar os tipos java.sql.ResultSet, java.sql.Connection, java.sql.PrepareStatement e outros!

Isso nos leva à segunda parte do código, exportsbr.com.esig.database.api está informando que, quem utilizar o módulo br.com.esig.database (Por meio de um requires) terá acesso aos tipos públicos presentes no pacote br.com.esig.database.api.

Grafo de módulos

A relação entre os módulos do Java é representado por um grafo, onde o sentido da seta indica quem lê um módulo. A estrutura modular do JDK pode ser visualizada como um grafo. A representação completa do grafo de módulos do JDK é representada com uma redução transitiva para facilitar o entendimento, omitindo arestas redundantes.

A visualização do grafo do módulo de banco de dados que estamos explorando seria o seguinte:

grafo-modulo-database.png

A imagem gerada pelo NetBeans nos reforça que, todos os módulos dependem implicitamente do java.base. Como nosso módulo declara um requires em java.sql, há uma aresta no sentido do nosso módulo para o java.sql. Ainda é possível analisar a presença de dois módulos não declarados, java.logging e java.xml. Isso acontece devido a resolução de módulos localizar dependencias adicionais necessárias para satisfazer todas as dependências necessárias ao projeto. Porém, há um detalhe no que diz respeito a visibilidade implicita ou visibilidade transitiva.

Visibilidade transitiva

Para explicar a visibilidade transitiva, vamos imaginar a utilização de nossos módulo recém criado. Teríamos uma aplicação com módulo br.com.esig.app, que para utilizar o nosso módulo iria indicar via module-info.java:

//module-info.java
module br.com.esig.app {
    requires br.com.esig.database;
}

// br.com.esig.app.MapeadorLinhaPessoa.java
import br.com.esig.database.MapeadorLinha;
import java.sql.ResultSet; // Erro aqui
public class PessoaMapeadorLinha implements  MapeadorLinha<Pessoa>{
    public Pessoa mapear(ResultSet res, int index) {...}
}
// PessoaMapeadorLinha.java:3: error: package java.sql is not visible
// import java.sql.ResultSet; 
//  (package java.sql is declared in module java.sql, 
//   but module br.com.esig.app does not read it)
// 1 error

Perceba que, ao compilar a classe MapeadorLinhaPessoa um erro de compilação ocorre. Embora a aplicação possa visualizar os tipos do módulo br.com.esig.database, ela não consegue visualizar os tipos do módulo java.sql. Essa visibilidade implicita é em muitas situações desejada. Isso, porque o módulo br.com.esig.database depende do java.sql, não apenas porque sua implementação contém tipos definidos nestes módulos, mas também por definir estes tipos em assinaturas de APIs públicas (Caso do parâmetro ResultSet do MapeadorLinha).

Para corrigir esta situação, precisamos definir uma visibilidade transitiva ao módulo java.sql para quem utilizar br.com.esig.database. Dessa forma, nosso módulo de banco de dados ficaria da seguinte forma:

module br.com.esig.database {
    requires transitive java.sql;
    exports br.com.esig.database;
}

Situação semelhante ocorre em outros módulos da própria plataforma, como vimos anteriormente, o java.sql necessita dos módulos java.logging e java.xml. Na API pública da interface java.sql.Driver, temos um método que retorna uma instância de Logger que é um tipo declarado e exportado no módulo java.logging. Dessa forma, a declaração correta do módulo deve utilizar requires transitive.

module java.sql {
    requires transitive java.logging;
    requires transitive java.xml;

    exports java.sql;
    exports javax.sql;
    exports javax.transaction.xa;
    ...
}

Compatibilidade com versões anteriores

Como citado anteriormente, o projeto só foi aplicado às regras de encapsulamento do JigSaw no momento da inclusão do module-info.java. Isso permite que aplicações existentes sejam migradas para uma arquitetura modular de forma flexivel e gradual. Quando uma aplicação executando no Java 9 necessita de um tipo que não está definido em nenhum módulo (Ex:Um JAR gerado em versões anteriores), o sistema de módulos tenta carregar o tipo pelo class path. Se for possível, então o tipo carregado é incluído em um módulo especial chamado unnamed module, para que dessa forma seja garantido que todos os tipos estejam associados com um módulo.

Um código de qualquer tipo presente no class path irá ser capaz de acessar todos os tipos exportados de todos os demais módulos construídos pela plataforma Java. Dessa forma, uma aplicação que foi compilada e que executa no Java 8 irá, portanto, compilar e executar da mesma forma no Java 9, desde que ele utilize apenas código da plataforma e classes não depreciadas.

Além disso, unnamed module exporta todos os seus pacotes. Isso significa que um código presente em um módulo pode acessar um tipo definido em um unnamed module desde que presente no class path. Por fim, um módulo não é capaz de declarar uma dependência sobre o unnamed module.

Conclusão

Nesta segunda parte dos fundamentos do Java Modular, foi apresentado como o Java 9 irá aplicar a modularização em nível de código, apresentando as instruções requires, exports e requires transitive. Além disso, foi apresentado a visão em grafo dos módulos e a problemática da visibilidade transitiva. Por fim, abordado alguns pontos sobre compatibilidade para execução de código com versões anteriores ao Java 9 e o conceito de unnamed module.

Se você gostou, deixe um comentário e compartilhe com seus amigos. Até a próxima.

Modularidade com Java 9 – Parte 1

Neste primeiro momento vamos falar sobre a motivação principal funcionalidade que será liberada no Java 9 que é o sistema de módulos chamado Project Jigsaw. O nome já diz tudo, Jigsaw em inglês significa Quebra-cabeça e essa é a ideia do Projeto!

Este texto é a primeira parte de um conjunto de publicações das anotações dos meus estudos sobre as novidades do Java 9. Vamos lá!

Motivação

A linguagem Java até o momento possui alguns modificadores de acesso que são utilizados para definir a visibilidade dos componentes que são public, protected, private e package. Quando desejamos desenvolver uma biblioteca que será disponibilizada via .jar precisamos compartilhar alguns vários componentes para todos e muito provavelmente precisamos reutilizar alguns destes componentes internamente entre os pacotes do nosso próprio código. Para isso nós precisamos definir nossa classe e métodos com o modificador de acesso public, já que nenhum dos outros modificadores permite este comportamento..

O problema é que desta forma estamos compartilhando com todos alguns artefatos que deveriam fazer sentido apenas dentro do módulo mas que precisaram ser públicos para permitir a comunicação entre pacotes. Isso afetou inclusive a própria plataforma que teve de expor as classes de implementação interna do JDK e que são utilizadas para construção de ferramentas como a objenesis que tem a finalidade de criar objetos sem invocar o construtor por meio de uma classe chamada sun.misc.Unsafe.

Embora a utilização de classes do pacotes sun.* não seja recomendada e Unsafe tenha sido projetada como um singleton de construtor privado e acessível via um método de acesso que verifica até o ClassLoader que está chamando o método, o pessoal consegue burlar este mecanismo de segurança aplicado ao singleton via reflection!

O código acima, em geral, define a visibilidade à um atributo privado da classe Unsafe chamado theUnsafe que é a referência singleton da classe! Esta classe permite realizar outras coisas bastante “estranhas” como por exemplo, lançar exceções verificadas em um método sem a necessidade de declarar a exceção, até coisas mais perigosas como atuar diretamente na alocação de memória nativa!

Sendo assim, o JDK 9 irá incluir um novo elemento na linguagem, que são os módulos, que vai expandir o entendimento da visibilidade ‘estática’ public em três situações:

  • público para todos
  • públic para módulos específicos
  • público apenas dentro do módulo.

Dessa forma o operador public não significa mais que sua classe estará acessível para toda JVM! Esta encapsulação forte é a possibilidade de um componente declarar quais dos seus tipos públicos serão acessíveis para outros componentes e quais não. Ou seja, o desenvolvedor não será capaz nem ao menos de importar uma classe de um pacote que não seja exportado, causando erro de compilação.

Esta funcionalidade irá beneficiar, além da própria implementação da plataforma Java que irá poder ocultar componentes e utilizá-los internamente sem preocupação, os desenvolvedores de aplicações, bibliotecas.

Ainda sobre a acessibilidade, a API de reflexão também irá respeitar as regras da imposta pelo sistema de módulos. As operações de reflexão irão realizar as mesmas checagens realizadas pelo compilador em tempo de execução. Ou seja, ao chamar métodos da API como o setAcessible(true) em um objeto qualquer, será verificado se a classe do objeto está em um módulo diferente e se o pacote que define a classe foi exportado para o módulo em questão. Com isso, situações de acesso indevido de objetos como a sun.misc.Unsafe não devem mais ocorrer.

Considerações finais

Vimos que alguns problemas enfrentados no encapsulamento de componentes no Java e como o Java pretende tratar esta situação com o uso do sistema de módulos introduzido no JDK 9. Nos próximos posts será descrito outros assuntos relacionados, entre eles:

  • Resolução de módulos e Grafo de módulos
  • Legibilidade e Acessibilidade Implícita de Componentes
  • Detalhes da sintaxe de um módulo.

Se você gostou, deixe um comentário e compartilhe com seus amigos. Até a próxima.