Design Pattern – Observer (Part 02)

Hello, how are you?

Let us keep talking about the Observer pattern. If you did not read the first post about this pattern, read it before reading the Part 2 about this pattern (Design Pattern – Observer (Part 01) ).

How could we loose coupling of our classes? (Questioning of the last post Design Pattern – Observer (Part 01) )

A good way to loose this coupling would be: the class Listener receives a reference to its Notifier class. It is a very simple solution.

Our Notifier class will only “tell” to a Listener that something happen through the “update” method, and the Listener will decide which attributes will be needed.

The class YahooPattern will need a password to send the email, it will access the Notifier the get this information. Let us begin the refactoring?

First of all we need to refactor the “update” method. Let us use another kind of super class? Let us update our UML and use an abstract super class (instead interface like we did in the last post).
Design Pattern - Observer (Part 02)Design Pattern - Observer (Part 02)
We updated our interface Notifier, now it has methods that will be visible to our listeners. The listeners now will be able to get the information that it is needed to send an email. The class that sends the email (EmailPattern) has a reference to the Notifier.

The Notifier reference will enable the emailSender to get all the needed data, instead the older version where the Notifier has the responsibility to know each of its listeners. If each listener change the needed parameters to send an email, the update method from the Notifier class will remain intact. If you had to change any email class to get another data, you only need to change the “collectData” method that is an abstract method which every subclass will implement.

If a new requirement comes to the HotmailPattern class, like add a specific image to the text, you would need to add the get method to the Notifier interface and update the HotmailPattern class to get and use this value. Notice that our class Notifier has been changed by adding a get method only, and the Notifier does not need to know whom/how will use this data.

Let us see our implemented code:

public class EmailSender implements Notifier {

    private List listeners;
    private String emailText;
    private String emailSubject;
    private String password;

    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();
        password = getPasswordFromDataBase();
        notifyListeners();
    }

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

    // Getters and Setters
}
// Our superclass EmailPattern. All Emails patterns will extend this class
public abstract class EmailPattern implements Listener{
    protected Notifier emailSender;

    public EmailPattern(Notifier emailSender){
        this.emailSender = emailSender;
    }

    public void update(){
        collectData();
        sendEmail();
    }

    protected abstract void collectData();
    protected abstract void sendEmail();
}
// This is our implementation. The class will get the password and any other data that it needs
public class YahooPattern extends EmailPattern {
    private String emailText;
    private String emailSubject;
    private String emailPassword;

    public YahooPattern(Notifier emailSender) {
        super(emailSender);
    }

    @Override
    protected void collectData() {
        emailText = emailSender.getEmailText();
        emailSubject = emailSender.getEmailSubject();
        emailPassword = emailSender.getPassword();
    }

    @Override
    public void sendEmail() {
        System.out.println("Sending email subject: " + emailSubject + " with the text: " + emailText + " with the password: " + emailPassword);
    }
}

And here comes a new Design principle:
“Always try to loose the coupling between classes that communicates.”
We loose the coupling when we updated our Notifier class to only provide the information, instead of knowing the need of each listener; now our Notifier just calls the listeners without worrying which parameters it should pass. And by updating the listener so it might be able to collect all the data it needs.

A briefing of the design patterns seen 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.

Design Pattern: Observer
Studied Principles:

  • Always try to loose the coupling between classes that communicates.

If you have any doubt or question, just ask.

I hope you liked this post, see you soon.

Design Pattern – Observer (Parte 02)

Pessoal, bom dia.

Hoje vamos continuar falando sobre o padrão Observer. Seria muito bom que você lesse a primeira parte, caso não o tenha feito Design Pattern – Observer (Parte 01).

Continuando o último post (Design Pattern – Observer (Parte 01)), como poderíamos desacoplar nossas classes?

Um bom modo para criar esse desacoplamento de métodos, seria: a classe Listener passe a ter uma referência da classe Notifier. Bem simples “né”?

Desse modo, a nossa classe Notifier, ela irá apenas dizer ao seu Listener que houve alteração em seu status e o Listener irá buscar todos os parâmetros que necessita.

Continuando no exemplo, que a classe YahooPattern a partir de agora necessitará de uma senha para enviar o email, nossa classe consultaria na classe Notifier os dados que necessitasse. Vamos começar as alterações?

Primeiramente precisamos alterar o método “update”. Para isso, que tal aplicarmos outro modo de “programar para interface”? Vamos atualizar nosso modelo e utilizar uma superclasse abstrata.

Design Pattern - Observer (Parte 02)Design Pattern - Observer (Parte 02)

Alteramos nossa interface Notifier, ela contém agora métodos que serão visíveis aos nossos listeners, para que os listeners possam buscar as informações que necessitarem. As classes que representam o envio de email (EmailPattern) apresentam uma referência do tipo Notifier.

Essa referência foi criada para que o Listener possa buscar os dados que ele quiser e não a Notifier seja o responsável por conhecer a necessidade de cada Listener. Sendo assim, cada Listener pode alterar os parâmetros necessários que nosso método update continuará o mesmo, a alteração seria no método “collectData” que toda classe do tipo EmailPattern é obrigada a construir. Com isso, caso a classe GmailPattern precisasse de senha, apenas ela sofreria alteração e mais ninguém.

Se fosse um novo requisito que a classe HotmailPattern utilizasse certa imagem, bastaria adicionar em nossa classe Notifier um método “get” (como forma acesso), para que a classe HotmailPattern utilizar esse valor. Note que a classe Notifier está sofrendo uma alteração apenas para fornecer uma informação, ela não estará interessada em quem/como estará utilizando essa informação.

Vamos ver nosso código final:

public class EmailSender implements Notifier {

    private List listeners;
    private String emailText;
    private String emailSubject;
    private String password;

    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();
        password = getPasswordFromDataBase();
        notifyListeners();
    }

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

    // Getters and Setters
}
// Our superclass EmailPattern. All Emails patterns will extend this class
public abstract class EmailPattern implements Listener{
    protected Notifier emailSender;

    public EmailPattern(Notifier emailSender){
        this.emailSender = emailSender;
    }

    public void update(){
        collectData();
        sendEmail();
    }

    protected abstract void collectData();
    protected abstract void sendEmail();
}
// This is our implementation. The class will get the password and any other data that it needs
public class YahooPattern extends EmailPattern {
    private String emailText;
    private String emailSubject;
    private String emailPassword;

    public YahooPattern(Notifier emailSender) {
        super(emailSender);
    }

    @Override
    protected void collectData() {
        emailText = emailSender.getEmailText();
        emailSubject = emailSender.getEmailSubject();
        emailPassword = emailSender.getPassword();
    }

    @Override
    public void sendEmail() {
        System.out.println("Sending email subject: " + emailSubject + " with the text: " + emailText + " with the password: " + emailPassword);
    }
}

Com isso, podemos chegar a mais um princípio de Design:
"Procure sempre diminuir o acoplamento em objetos que se comuniquem."
Nós realizamos esse desacoplamento ao simplesmente fazer com que nossa classe Notifier possa fornecer dados, mas não entender o que cada Listener necessita. Ela apenas coleta todos os dados e chama cada Listener, e o Listener por sua vez é quem sabe qual informação utilizar.

Um resumo do visto até hoje:
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.

Padrão de Projeto: Observer
Princípios vistos:

  • Procure sempre diminuir o acoplamento em objetos que se comuniquem.


Qualquer dúvida/colocação basta escrever.

Espero que você goste, até a próxima.

Revisado por: Renan Alves dos Santos