Auto Create Schema Script with: Ant, Hibernate 3 and JPA 2

Hello, how are you?

Have you noticed how boring is to create sql scripts by yourself? It is complicated to a developer to know the syntax of a “create table” to each database (Oracle, MySQL, …). Let us say that sometimes it is hard to remember how to declare a String to use in the Oracle, Microsoft SQL Server, MySQL and others.

To make our life easier about scripts we have the Hibernate to help us with this script creation.

The Hibernate can help us in two ways when we talk about sql: creating the scripts files and doing an automatic update in the database – without the scripts.

Talking very fast about the automatic way:
The automatic way was implemented in the last post, Tutorial Hibernate 3 with JPA 2. You just have to add the bellow code in your “persistence.xml” that your app will automatically update the database:

<property name="hibernate.hbm2ddl.auto" value="update"/>

Just to remember: “It is not a good idea to leave the above code activated in a production environment. You will be updating important data that may raise some exception. The ideal is to have a script file to do this database updating. By example, with the script file, a DBA will have the opportunity to see the impact of the script in the database.”
Automatically creating a script with the schema reduces the bug chances created by developers. Some of the usual errors are: scripts without the schema, some primary key identifier, FK, syntax error and others.

Let us finish the chatting and start with some code. We will use the same code created in the first post about Business: Tutorial Hibernate 3 with JPA 2.

We will work with a tool named Ant. It is nothing more that a task handler. It executes takes correctly listed in a configuration file. Let us start by creating this file.

Create a file name “build.xml” in the project root. Right button click in the project name > New > File. Put the file name and click “Finish”.

Auto Create Schema Ant Hibernate 3 e JPA 2
Double click in the created file to start its edition.

Let us create a simple Hello World to test the ant. Take a look how our first code will look like:

<project name="HibernateJPA" basedir=".">
    <target name="Hello">
        <echo message="Hello World Ant!"  />
    </target>
</project>

Notice that we created something like a tag, named “target”. “Target” is an ant action, this means that, every ant action must be inside a “target”. We can have an unlimited targets number in each file.

To finish this Hello World, let us add our file “build.xml” in the ant view (if you do not know how to open a view, in this link there is a sample Creating a WebServer). Just drag and drop the “build.xml” file inside the “Ant” view. Double click in the Hello target and our message will be displayed in the Console view.

Auto Create Schema Ant Hibernate 3 e JPA 2
Auto Create Schema Ant Hibernate 3 e JPA 2

We have to do the Hibernate Tools download; the Hibernate Tools is the be responsible to create the script file for us. I downloaded the jar file that you can find it in here (Hibernate Tools), but if you want to download in the Hibernate site, you will need to download the JBoss Tools; inside it you will find the Hibernate Tools.

Uncompress the .zip file and put the .jar file inside the lib folder in our system (if you have any problem, in the first post we do this unzip process step by step, Tutorial Hibernate 3 with JPA 2).

We need to add our lib in the Build Path (If you do not know how to do it, you can take a look at the first post and read how this process is done, Tutorial Hibernate 3 with JPA 2).

Auto Create Schema Ant Hibernate 3 e JPA 2
We need to add our application paths in the “build.xml” file:

<project name="HibernateJPA" basedir=".">
    <property name="compiledFiles" location="bin" />
    <property name="libraries" location="lib" />
    <property name="sql" location="sql" />

    <path id="lib.path">
        <pathelement location="${libraries}" />
        <fileset dir="${libraries}">
            <include name="*.jar" />
        </fileset>
    </path>

    <target name="Hello">
        <echo message="Hello World Ant!"/>
    </target>
</project>

In the build.xml file we added the libraries path, the compiled files path and a folder path that does not exists yet, “sql”. A little Eclipse detail is that it has a configuration that hides the “bin” folder from your eyes. If you want to see it through the Windows you can find it in, “…/workspace/yourProject/bin”.

And now let us see the code that will create the “.sql” file:

<project name="HibernateJPA" basedir=".">
    <property name="compiledFiles" location="bin" />
    <property name="libraries" location="lib" />
    <property name="sql" location="sql" />

    <path id="lib.path">
        <pathelement location="${libraries}" />
        <fileset dir="${libraries}">
            <include name="*.jar" />
        </fileset>
    </path>

    <target name="Create MySchema" description="Export schema to DDL file">
        <taskdef name="hibernatetool" classname="org.hibernate.tool.ant.HibernateToolTask" classpathref="lib.path" />
        <mkdir dir="${sql}" />
        <touch file="${sql}/script.sql" />
        <hibernatetool destdir="sql">
            <jpaconfiguration persistenceunit="Hello" />
            <classpath>
                <path location="${compiledFiles}" />
            </classpath>
            <hbm2ddl outputfilename="script.sql" format="true" export="false" drop="true" />
        </hibernatetool>
        <echo message="The file was saved in: ${sql}" />
    </target>    

    <target name="Hello">
        <echo message="Hello World Ant!"/>
    </target>
</project>

After you execute the task, you will see a message at the Console view and it will show where our file was saved (click on the project with the left button > “Refresh”; the folder “sql” will appear).

Auto Create Schema Ant Hibernate 3 e JPA 2
You may think: “I still have to put the schema in the created sql file by myself. This file does not help me in nothing.”

To finish this post, let us change a configuration file so the Hibernate will create the script file for us with the schema. Let us add the next line inside our “persistence.xml”:

<property name="hibernate.default_schema" value="Hello"/>

With the above line, the Hibernate will create the sql file with a default schema. Our file “persistence.xml” will look like:

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="Hello" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <properties>
            <property name="hibernate.show_sql" value="true" />
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/Hello"/>
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="root" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.default_schema" value="Hello"/>
    </persistence-unit>
</persistence>

Notice that we removed the code that updates the database automatically. Just to make it clear that once we have the database script we do not need an automatically update in the database.

Let us run again our target and re-create the sql file. You will see that our file will be created with the default schema in it.

If you want to create a script to another database, you just have to change the properties values: “javax.persistence.jdbc.driver” and “hibernate.dialect”. You just have to download the right connectors and done!

I hope this post can be useful to you.

Any doubts just post it.

See you soon.

Auto Create Schema Script com: Ant, Hibernate 3 e JPA 2

Olá, tudo bem?

Já reparou como é chato criar script na unha? É complicado o desenvolvedor ter que saber a sintaxe de um “create table” para cada banco de dados. Digamos que as vezes é dificil se lembrar de como declarar uma coluna String para Oracle, ou Microsoft SQL Server, MySQL e assim vai.

Para melhorar essa situação podemos contar com a ajuda do Hibernate para gerar esse script por nós.

O hibernate pode nos ajudar com o banco de dados de dois modos: gerando arquivos do tipo sql e realizando a atualização de modo automático – sem scripts.

Resumindo o modo automático:
O modo automático foi mostrado no post Tutorial Hibernate 3 com JPA 2. Basta adicionar o código abaixo no “persistence.xml” que sua aplicação irá automaticamente atualizar o banco de dados:

<property name="hibernate.hbm2ddl.auto" value="update"/>

Volto a lembrar: "Não é aconselhável deixar essa opção ativa em produção. Você estará atualizando dados importantes e que não podem ser perdidos. O ideal seria um arquivo contendo os scripts e ele fazendo a atualização do banco de dados. Por exemplo, com o arquivo, um DBA poderá analisar o impacto da alteração de modo mais claro."

Gerar automaticamente o script inclusive com o schema elimina em parte o erro humano. Alguns erros comuns são: falta do schema no script, algum identificador de chave primária, FK, erro de sintaxe e assim vai.

Chega de papo e vamos aos códigos. Estaremos utilizando exatamente o código do primeiro post desse assunto de Business: Tutorial Hibernate 3 com JPA 2.

Vamos trabalhar com uma ferramenta chamada Ant. Nada mais é do que um executador de tarefas. Ele executa as tarefas listadas em um arquivo corretamente configurado. Vamos começar por esse arquivo.

Crie um arquivo chamado “build.xml” na raiz do projeto. Botão direito em cima do nome do projeto > New > File. Coloque o nome e clique em “Finish”.

Auto Create Schema Ant Hibernate 3 e JPA 2
Dê um duplo clique no arquivo criado para começar sua edição.

Iremos criar um simples “Hello World” para testar o funcionamento. Abaixo veja como ficará o primeiro código:

<project name="HibernateJPA" basedir=".">
    <target name="Hello">
        <echo message="Hello World Ant!"  />
    </target>
</project>

Veja que criamos algo chamado “target”. O “target” é uma ação no ant, ou seja, toda ação realizada tem que estar dentro de um “target”. Podemos ter quantos quiser.

A mensagem de Hello World será exibida em nosso console.

Por último, vamos adicionar a view do ant o nosso arquivo “build.xml” (caso você não saiba, veja como adicionar uma view aqui Criando um WebServer). Para adicioná-lo, você terá que arrastar o arquivo “build.xml” para dentro da view “Ant”. Dê um duplo clique em cima da target Hello e nossa mensagem será exibida na view do Console.
Auto Create Schema Ant Hibernate 3 e JPA 2
Auto Create Schema Ant Hibernate 3 e JPA 2

Precisamos fazer o download do Hibernate Tools que é o responsável por gerar o código. Eu utilizei o jar encontrado aqui (Hibernate Tools), mas caso queira pegar no site do Hibernate, você terá que baixar o JBoss Tools; e dentro dele você encontrará o Hibernate Tools.

Descompacte o arquivo .zip e coloque a biblioteca .jar dentro da nossa pasta lib (caso tenha problemas no nosso primeiro post esse processo é feito detalhadamente. Tutorial Hibernate 3 com JPA 2).

Precisamos agora adicionar nossa lib ao Build Path (Caso tenha dúvidas de como fazer, no primeiro post tem a explicação passo a passo Tutorial Hibernate 3 com JPA 2).
Auto Create Schema Ant Hibernate 3 e JPA 2

Vamos adicionar em nosso arquivo “build.xml” os diretórios necessários:

<project name="HibernateJPA" basedir=".">
    <property name="compiledFiles" location="bin" />
    <property name="libraries" location="lib" />
    <property name="sql" location="sql" />

    <path id="lib.path">
        <pathelement location="${libraries}" />
        <fileset dir="${libraries}">
            <include name="*.jar" />
        </fileset>
    </path>

    <target name="Hello">
        <echo message="Hello World Ant!"/>
    </target>
</project>

Veja que foram adicionados os caminhos onde o Ant encontrará as bibliotecas, os arquivos compilados e nossa futura pasta do SQL. Um detalhe é que o Eclipse, dependendo de como está configurado, pode esta escondendo a pasta “bin” dos seus olhos. Mas caso você escolha ver pelo Windows encontrará a essa pasta dentro do seu sistema, na pasta do …/workspace/seuprojeto/bin.

E por último, vamos ver o esperado código que gera o arquivo “sql”:

<project name="HibernateJPA" basedir=".">
    <property name="compiledFiles" location="bin" />
    <property name="libraries" location="lib" />
    <property name="sql" location="sql" />

    <path id="lib.path">
        <pathelement location="${libraries}" />
        <fileset dir="${libraries}">
            <include name="*.jar" />
        </fileset>
    </path>

    <target name="Create MySchema" description="Export schema to DDL file">
        <taskdef name="hibernatetool" classname="org.hibernate.tool.ant.HibernateToolTask" classpathref="lib.path" />
        <mkdir dir="${sql}" />
        <touch file="${sql}/script.sql" />
        <hibernatetool destdir="sql">
            <jpaconfiguration persistenceunit="Hello" />
            <classpath>
                <path location="${compiledFiles}" />
            </classpath>
            <hbm2ddl outputfilename="script.sql" format="true" export="false" drop="true" />
        </hibernatetool>
        <echo message="The file was saved in: ${sql}" />
    </target>    

    <target name="Hello">
        <echo message="Hello World Ant!"/>
    </target>
</project>

Após executar, será exibida uma mensagem exibindo o local em que o arquivo foi salvo (clique sobre o projeto e selecione “Refresh” e a pasta sql irá aparecer).
Auto Create Schema Ant Hibernate 3 e JPA 2

Mas talvez você ainda esteja pensando: “Uau, grande coisa. O script não foi gerado com o schema, vou ter que colocar tudo manualmente. Não me ajuda em nada.”

Para fechar, vamos fazer com que o script seja gerado já apontando o schema também. Vamos adicionar a seguinte linha ao nosso “persistence.xml”:

<property name="hibernate.default_schema" value="Hello"/>

Com essa linha de comando o Hibernate irá gerar o script com um schema padrão. Nosso arquivo “persistence.xml” ficará assim:

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="Hello" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <properties>
            <property name="hibernate.show_sql" value="true" />
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/Hello"/>
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="root" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.default_schema" value="Hello"/>
    </persistence-unit>
</persistence>

Repare que o código que estava antes, o que alterava o banco de de dados automaticamente, foi retirado. Apenas para deixar claro que uma vez que temos um script gerado não existe motivo para um update automático, realizado pela aplicação, no banco de dados.

Vamos gerar novamente o arquivo de script. Veja agora que nosso script será criado com o schema padrão na frente.

Caso queira gerar o script para outro banco de dados, basta alterar os valores das chaves “javax.persistence.jdbc.driver” e “hibernate.dialect”. Faça o download dos conectores devidos e pronto.

Espero que esse post seja útil a você.

Qualquer comentário/dúvida/crítica basta postar abaixo.

Até a próxima.

Revisado por: Renan Alves dos Santos

Design Pattern – Observer (Part 01)

Hello, how are you?

Today we will talk about another Design Pattern, “Observer”. If you want to see the first design pattern that we studied you can see it here (Strategy). Let us do a fast review about all design pattern subject that we have studied so far:

      Design Pattern: Strategy

 

    Studied Principles:

  • Identify everything that changes and isolate it from what does not change.
  • Program to an interface.
  • Give priority to composition rather than inheritance.

Let us work with the following user case:
“Mail4Usted is a company that sends emails to the clients that are recorded in its database. The company has a system that applies specifics configurations to each email server, there are a specific configurations for emails like Gmail, Hotmail.


The Mail4Usted is requesting a refactoring in its system. The problem is that the last company that was doing the maintenance of the company system has disappeared; everything that the Mail4Usted can do is to send the last version of the software code.

The system sends email only to two email servers: Gmail and Yahoo. The Mail4Usted wants its system to send email to the Hotmail customers; if the Mail4Usted likes the job new requests will be done.
Our newest customer also explains that this division for the email servers is needed because of the specific configuration that it is applied for each email server.”

The code that we received is written bellow:

public class EmailSender{
    public void sendNewEmails(){
        String emailText = getNewEmailTextFromDataBase();
        String emailSubject = getNewEmailTitleFromDataBase();

        sendEmailToGmail(emailText, emailSubject);
        sendEmailToYahoo(emailText, emailSubject);
    }

    //Others methods
}

A very simple and pour solution would be, to write the method that sends email to the Hotmail and we would be happy:

public class EmailSender{
    public void sendNewEmails(){
        String emailText = getNewEmailTextFromDataBase();
        String emailSubject = getNewEmailTitleFromDataBase();

        sendEmailToGmail(emailText, emailSubject);
        sendEmailToYahoo(emailText, emailSubject);

        // Our new method
        sendEmailToHotmail(emailText, emailSubject);
    }

    //Others methods
}

Imagine if every time that an email server had to be added or receive some maintenance in the system, our main class will be edited. Our main class would be long, hard to maintain and with none cohesion.

To make our code better we could apply two design patterns: starting with Strategy and then the Observer.

What always change in our code that we could isolate? Notice that we have a behavior that is always changing and every refactor that we have to do will affect our main class; this is something that does not need to happen.

Applying the concepts of the Strategy pattern we will isolate the behavior that always changes. But to do things more interesting, what if we understand a little bit about the Observer pattern so we might apply the two patterns together? Let us see a little history that will give us the idea of how the Observer pattern works:

“Imagine that you subscribed in a store to receive its announcements about some products with discounts. Even if you do not have any idea of when you can buy these products with discounts, you will receive the announcement.
Notice that you will be like a “target” of these notifications even if you are busy with others occupations. You will receive the announcements no matter if you are reading the news paper, watching TV or walking with the dog.”

What if we apply this “announcement” idea here? We could write the code isolating all send email behavior (Strategy). After we isolate the code that varies into a class, this class will receive like a notification informing that it already has the needed data (working like a Listener). We will be applying the Observer pattern that works like this: “The class A notifies to the class B needed information, the class B will work with this information”.

We could define the Observer pattern by:

“Defines a dependency relationship “One to Many”. When this objects state changes, all others objects with this relationship will be notified about this changing.”
To apply this concept in our user case we will use the class EmailSender to notify each class EmailHoster that a new routine to send email will be able to begin.

First let us take a look in how our interfaces will be (remember to always code to interfaces, and interfaces means a super type!):Design Pattern Observer Parte 01
With this interfaces we will have a code more flexible to refactoring, with good design patterns applied.

The Listener will be notified that some changing has happened (update(…) method) in the stated of our object “Notifier” (that it is the responsible to notify). After the object Listener receive this notification, it will send the emails (sendEmail() method).

Our classes of the type Listener and EmailPattern:Design Pattern Observer Parte 01

Our class Notifier:

Design Pattern Observer Parte 01
We have isolated all behaviors that changes (Strategy). We removed the code from the principal class, we have now a class that will send the email (EmailPattern classes); this “send email” code will be inside the class EmailPettern. We also have a class to set up the info and give to the EmailPattern classes (EmailSender).

Take a look how our classes will be coded:

public class EmailSender implements Notifier {
    private List listeners;
    private String emailText;
    private String emailSubject;

    public EmailSender(){
        listeners = new ArrayList();
    }

    public void addListener(Listener listener){
        listeners.add(listener)
    }

    public void removeListener(Listener listener){
        listeners.remove(listener)
    }

    public void createEmails(){
        emailText = getNewEmailTextFromDataBase();
        emailSubject = getNewEmailTitleFromDataBase();

        notifyListeners();
    }

    public void notifyListeners(){
        for(Listener listener : listeners){
            listener.update(emailText, emailSubject);
        }
    }

    //Others methods
}
// All classes would have almost the same implementation with their own peculiarities.
public class GmailPattern implements Listener, EmailPattern {
    public void update(String emailText, String emailSubject){
        // apply class peculiarities
        sendEmail();
    }

    public void sendEmail(){
        // send the email
    }
}

I only wrote above the code of the GmailPattern to do the reading easier. All others classes like HotmailPattern, YahooPattern would have the same methods with not equal behaviors.

You can see how cleaner our code is, with more cohesion and functional. No matter which kind of email we are working with (GmailPattern, YahooPattern or HotmailPattern), our class of the type Notifier will notify to its Listener that some changing happened in its state. Our class Notifier does not need to know anymore which kind of email server (Gmail, Yahoo, Hotmail …) it is working with. Can you see how our class EmailSender still working with the emails in a “cleaner” way?

Notice that if we need to change the method that configures the Hotmail html, our class EmailSender will not receive any edition. Isolating this behavior we are allowing our software to be more flexible to changes.

You may be thinking: “What is the advantage of the Observer pattern? Where can I apply this?” I will write some samples below:

  • Every time a car passes through a Stop light without stopping, some machine will take a picture and send it to the system. There is “somebody” that “saw” this car passing and notified to the system what happened.
  • An application that sends online messages. Let us to study some messenger: the old ICQ (but work the same with the messengers of our days) where you need to have the app in your pc. You can send a message by selecting a buddy, typing some text and sending it. The ICQ notifies the server delivering this message. Or the ICQ of your buddy will search for undelivered messages there or the server will notify the user ICQ in his PC. One thing is for sure, no matter what your buddy is doing: “reading soap opera news”, or “watching Tom & Jerry cartoons on Youtube”, he will receive this message.
  • One simpler sample to finish: a TV remote control. There is an application waiting for the stimulus that comes from the control. When you push the button the application understand the action you requested and execute it.

But we still having a strong coupling between our class EmailSender and its Listeners (GmailPattern…). Take a look at the code above and try to figure it out…

To let this coupling easier to see, let us take a look in a new user case:
“Image now that more than the subject and the email text (body text), it will necessary to inform which Yahoo account we will use. With this new user case, we will have a Listener with different behavior of the others.”
Every time we need to change the amount of parameters to send to a Listener, like YahooPattern, our Notifier (EmailSender) will have to change too.

How can we work in this coupling? The ideal is when you edit one class, this editing do not affect any other.

We will see in the next post about Design Patterns a solution to louse this coupling.

See you soon! o_

Design Pattern – Observer (Parte 01)

Olá pessoal, tudo bem?

Hoje vamos ver sobre o Padrão de Projeto “Observer”. Caso queira ver sobre o primeiro padrão, Strategy, clique aqui. Apenas para revisar vamos ver o pequeno quadro abaixo que exibe o que já foi estudado sobre o assunto de Design Pattern:
Padrão de Projeto: Strategy
Princípios vistos:

  • Identifique tudo o que varia e separe do restante que não irá variar.
  • Programe para interface.
  • Dar prioridade à composição.

Vamos imaginar o seguinte caso de uso:
A empresa mail4Usted de mala direta tem um sistema especializado em enviar emails. A empresa tem um sistema que aplica configurações específicas para cada tipo de servidor de email.
A mail4Usted solicita que seja efetuada uma alteração em seu sistema. O problema é que a antiga empresa que dava manutenção em seu sistema sumiu do mapa, então o máximo que a mail4Usted poderá fazer é fornecer o código fonte antigo.

O sistema atualmente envia email apenas para clientes que tenham email no Yahoo, e Gmail. Foi solicitado pela empresa que seja adicionado primeiramente envio de email para o Hotmail, se a mail4Usted gostar do trabalho realizado fechará contrato para adicionar outros provedores de email.

Nosso mais novo cliente também explica que essa divisão de email é necessária, pois uma formatação especial para cada servidor de email é aplicada.”

O código que nos foi deixado é o seguinte:

public class EmailSender{
    public void sendNewEmails(){
        String emailText = getNewEmailTextFromDataBase();
        String emailSubject = getNewEmailTitleFromDataBase();

        sendEmailToGmail(emailText, emailSubject);
        sendEmailToYahoo(emailText, emailSubject);
    }

    //Others methods
}

Em uma solução pobre, bastaria implementar o método para enviar email ao Hotmail e estaríamos felizes:

public class EmailSender{
    public void sendNewEmails(){
        String emailText = getNewEmailTextFromDataBase();
        String emailSubject = getNewEmailTitleFromDataBase();

        sendEmailToGmail(emailText, emailSubject);
        sendEmailToYahoo(emailText, emailSubject);

        // Our new method
        sendEmailToHotmail(emailText, emailSubject);
    }

    //Others methods
}

Mas imagine se toda vez que um servidor de email tiver que ser adicionado ou sofrer manutenção, nossa classe principal será alterada. Ficará uma classe longa, de difícil manutenção e não muito coesa.

Para melhorar poderemos aplicar dois padrões de projeto: a começar pelo Strategy e depois Observer.

O que varia em nosso código que poderíamos isolar? Note que temos um comportamento que sempre será diferente e toda alteração que acontecer afetará nossa classe principal, algo que não precisa acontecer.

Aplicando o princípio visto no Strategy iremos separar esse comportamento. Mas para deixar ainda mais interessante, que tal entender um pouco sobre o padrão Observer para aplicar os dois juntos? Vamos ver um pequeno exemplo para nos dar uma idéia sobre como padrão Observer funciona:
Imagine que você se cadastra em uma loja para receber anúncios de promoções sobre seus produtos. A partir de então mesmo que você não tenha a mínima idéia de quando acontecerá uma promoção, você irá receber essa notificação.
Perceba que você será alvo de uma notificação de promoção mesmo estando totalmente ocupado com outras atividades. Talvez você esteja lendo um jornal, vendo TV, passeando com o cachorro e mesmo assim receberá essa notificação.

Por que não aplicarmos essa idéia aqui? Poderíamos fazer com que cada comportamento de enviar email fosse isolado (Strategy). Depois que o comportamento for isolado em uma classe, essa classe irá receber uma notificação informando que ela já tem os dados solicitados (funcionando como um bom Listener). Veja que será aplicado o padrão Observer que funciona assim: a ClasseA informa à ClasseB uma informação necessária e a ClasseB irá trabalhar com essa informação.

Poderíamos definir o padrão Observer por:
Define uma relação de dependência “Um para Muitos”. Quando o estado desse objeto mudar, todos os outros relacionados a ele serão notificados dessa mudança.
Em nosso caso utilizaremos a classe do tipo EmailSender para notificar a cada classe do tipo EmailHost que acontecerá uma nova rotina para envio de email.

Primeiro vamos ver como ficarão nossas interfaces (lembre-se de sempre programar para interfaces, sendo que interfaces significam um super-tipo!):Design Pattern Observer Parte 01Teremos a partir de agora um código que será mais flexível a mudanças, com bons padrões começando a ser aplicados.

O Listener será notificado de que alguma mudança aconteceu (método update(…)) no estado de nosso “Notifier” (que é o responsável por notificar). Após perceber essa mudança, nosso listener irá enviar o email (método sendEmail()).

Nossas classes do tipo Listener e EmailPattern:Design Pattern Observer Parte 01

Nossa classe do tipo Notifier:Design Pattern Observer Parte 01
Note que isolamos vários comportamentos que variam (Strategy). Isolamos “quem” irá enviar email (classes EmailPattern) e “quem” irá preparar e notificar essas classes (EmailSender).

Veja agora como ficarão as nossas classes implementadas:

public class EmailSender implements Notifier {
    private List listeners;
    private String emailText;
    private String emailSubject;

    public EmailSender(){
        listeners = new ArrayList();
    }

    public void addListener(Listener listener){
        listeners.add(listener)
    }

    public void removeListener(Listener listener){
        listeners.remove(listener)
    }

    public void createEmails(){
        emailText = getNewEmailTextFromDataBase();
        emailSubject = getNewEmailTitleFromDataBase();

        notifyListeners();
    }

    public void notifyListeners(){
        for(Listener listener : listeners){
            listener.update(emailText, emailSubject);
        }
    }

    //Others methods
}
// All classes would have almost the same implementation with their own peculiarities.
public class GmailPattern implements Listener, EmailPattern {
    public void update(String emailText, String emailSubject){
        // apply class peculiarities
        sendEmail();
    }

    public void sendEmail(){
        // send the email
    }
}

Coloquei apenas o código do GmailPattern descrito acima para facilitar na leitura. Todas as outras classes como HotmailPattern, YahooPattern teriam os mesmos métodos variando apenas seu comportamento.

Veja como fica mais simples, coeso e funcional. Independente de qual o tipo de padrão do email a ser enviado (Gmail, Hotmail, Yahoo), nossa classe Notifier está notificando ao seu Listener que houve uma alteração de estado. Ela não precisa saber mais se agora estamos enviando para Gmail, Yahoo ou Hotmail. Viu como a classe EmailSender continuar realizando seu trabalho e de um modo mais “limpo”?

Repare que caso seja necessário atualizar o modo como a classe HotmailPattern envia o email, nossa classe EmailSender não sofrerá qualquer tipo de alteração. Isolando esse comportamento estamos tornando nosso programa mais flexível a mudanças.

Talvez você se pergunte: “Qual a vantagem do padrão Observer? Onde aplicar isso?” Vou colocar abaixo alguns exemplos:

  • Toda vez que um carro furar sinal vermelho, uma máquina irá tirar uma foto e enviar para o sistema. Teve que ter “alguém” que observou o fato e notificou ao sistema o que aconteceu.
  • Uma aplicação de mensagens online. Por exemplo: o antigo ICQ (mas vale para os atuais também), você tem a aplicação rodando em seu computador. Ao selecionar um contato de sua lista, você pode digitar uma mensagem e enviar. O ICQ em sua máquina notifica ao servidor essa mensagem. Ou o ICQ do contato busca no servidor se existem mensagens ou o servidor notifica o ICQ na máquina do usuário. Uma coisa é certa, não importa o que o seu contato esteja fazendo: “lendo fofoca de novela”, “vendo desenhos do Tom e Jerry no Youtube”, ele irá receber a mensagem.
  • Um mais simples ainda é: controle remoto. Existe uma aplicação que está aguardando o estímulo do controle. Assim que você pressiona o botão a aplicação entende a mensagem enviada e faz o que foi solicitado.

Mas ainda existe um acoplamento muito forte entre nossa classe EmailSender e suas listeners (GmailPattern,…). Olhe novamente o código mais acima e tente imaginar…

Para deixar esse acoplamento mais claro, vamos imaginar mais um requisito em nosso sistema:
Imagine que agora além da informação do “assunto” e do “conteúdo do texto” será necessário passar qual a conta do Yahoo usar. Com isso teremos um listener com um comportamento diferente dos demais.
Toda vez que precisarmos alterar a quantidade de parâmetros para enviar emails um Listener, YahooPattern por exemplo, terá que ser alterado e também um Notifier (EmailSender) sofrerá alterações.

Como melhorar isso? O ideal não é fazer com que alteração em uma classe não afete outra?

Veremos no próximo post sobre o assunto de Design Pattern outra solução para o tipo Observer.

Até a próxima pessoal. o_

Revisado por: Renan Alves dos Santos