Padrões de design - Um guia rápido para o padrão Observer.

Padrão de observador é um padrão muito usado. De fato, é tão comum que está sendo padronizado em muitas linguagens / bibliotecas de programação. Em Java, existe injava.util.Observer (descontinuado no Java 9). No Python, o mais próximo possível do apip é instalar o observador de padrões. Em C ++, às vezes podemos usar a biblioteca de reforço, mais precisamente #include . No entanto, é amplamente utilizado na indústria como uma solução personalizada. Para poder usá-lo corretamente e entender sua complexidade, precisamos nos aprofundar e explorá-lo.

O padrão de observador é classificado entre os padrões de design comportamental. Os padrões de design comportamental preocupam-se mais especificamente com a comunicação entre classes / objetos. [por Design Patterns explicado simplesmente]

O que é um padrão de observador? Além de um monitor ambulante que transmite televisão analógica (como na figura). O objetivo do padrão é definir um relacionamento um para muitos, de modo que, quando um objeto muda de estado, os outros são notificados e atualizados automaticamente. Mais precisamente, deseja ser informado sobre os eventos que acontecem no sistema. Vamos juntar as peças do quebra-cabeça em três etapas.

Etapa 1 - Palavras-chave

Definir palavras-chave é a receita secreta desta série de guias rápidos. Esse método me ajudou a entender verdadeiramente os padrões de design, codificá-los em minha mente e compreender as diferenças entre outros padrões de design.

  1. Assunto: É considerado o detentor de informações, dados ou lógica de negócios.
  2. Registrar / Anexar: Os observadores se registram no assunto porque desejam ser notificados quando houver uma alteração.
  3. Evento: Os eventos atuam como um gatilho no assunto, de forma que todos os observadores sejam notificados.
  4. Notificar: Dependendo da implementação, o sujeito pode "enviar" informações aos observadores ou os observadores podem "extrair" se precisarem de informações do assunto.
  5. Atualização: os observadores atualizam seu estado independentemente de outros observadores, no entanto, seu estado pode mudar dependendo do evento acionado.

Etapa 2 - Diagrama

Vamos dividir esse design em diferentes classes para simplificar um pouco.

  • Os ConcreteObservers são classes que contêm informações específicas para a instância atual. A função de atualização é chamada pela operação de notificação () do sujeito. Os observadores atualizam independentemente com base em seu estado atual.
  • O Observador é a classe principal dos observadores concretos. Ele contém uma instância do assunto. Quando um observador é inicializado, ele se registra / se anexa ao sujeito.
  • A classe Subject possui uma lista ou uma coleção de observadores. Quando um evento é acionado, ele chama a operação notify () que percorre todos os observadores, chamando sua função de atualização.

Etapa 3 - Codificar por exemplo

Sugiro copiar a classe de código por classe do meu repositório git "Andreas Poyias" ou os trechos abaixo (na ordem fornecida) e colá-los em qualquer um dos editores de C ++ on-line disponíveis, como c ++ shell, jdoodle, onlineGDB e run para observar a saída. Em seguida, leia os comentários ou a descrição abaixo. Não se apresse, leia atentamente (isso significa um minuto, não menos e nem mais).

Exemplo: considere um jogo de futebol. Muitos torcedores estão assistindo o jogo. Dividimos os apoiadores em duas categorias por idade, jovens e idosos. Quando a equipe marca um gol, os torcedores reagem de maneira diferente, de acordo com a idade e o nível de emoção.
Agora, vamos falar dos termos usados ​​para o padrão de observador:

  • O jogo é o assunto e os torcedores são os observadores.
  • Todos os observadores são apegados / registrados ao sujeito e são notificados quando o time de futebol marca (o evento-gatilho é o caso do time).
  • Os observadores atualizam seu comportamento, dependendo da notificação recebida.

Sujeito
Para esta aula, precisamos acessar uma lista de observadores. Quando os observadores estão prestes a se registrar, eles chamam a função anexar (esta) para adicionar-se à lista disponível (esta é uma instância do observador). Quando um evento é acionado, wenotify () todos os observadores para atualizar independentemente seu estado. Neste exemplo, o gatilho é se o time de futebol do observador marcou.

#include 
#include 
usando espaço para nome std;
assunto da classe {
    observadores de vetor ;
    bool marcado; // gatilho, evento
público:
    // registrar observadores
    Anular anexo (Observer * obs) {
        observers.push_back (obs);
    }
   
   // Este é o EVENTO
   // defina o if score e notifique TODOS os observadores
   void setScored (Pontuação booleana) {
      pontuação = pontuação;
      notify ();
   }
bool getScored () {
      retorno marcado;
   }
   // notifica implementação está mais abaixo
   // para que o script compile e execute
   void notify ();
};

Observador
Esta classe depende do assunto em que está registrada. Quando os observadores de concreto são inicializados, eles se apegam ao Assunto. Neste exemplo, o estado de cada observador é a sua emoção ao longo do jogo.

classe Observador
{
    Assunto * sujeito;
    int excitationLevel; // Estado
  público:
    Observer (Assunto * mod, int excLevel)
    {
        subj = mod;
        excitementLevel = excLevel;
        // Observadores se registram / se anexam ao Assunto
        subj-> anexar (isto);
    }
    atualização de vácuo virtual () = 0;
  protegido:
    Assunto * getSubject () {
       retorno subj;
    }
    void setExcitementLevel (int excLevel) {
       excitementLevel = excLevel;
    }
    int getExcitementLevel () {
       return excitationLevel;
    }
};

Esta é a declaração Subject :: notify () e, como mencionamos antes, seu trabalho é notificar todos os observadores para atualizar seu estado.

void Subject :: notify () {
  for (int i = 0; i  update ();
}

Observadores de concreto
Os observadores concretos herdam da classe Observer e todos devem ter a função de atualização. Neste exemplo, os observadores concretos são distinguidos entre jovens e velhos apoiadores. Se o nível de excitação for muito alto, os apoiadores mais velhos correm o risco de ataques cardíacos e os mais jovens correm o risco de beber e dirigir. O estado deles é atualizado de forma independente, como provaremos na função principal mais adiante.

classe Old_ConcreteObserver: public Observer
{
   público:
     // Chama o construtor pai para registrar com o assunto
     Old_ConcreteObserver (Assunto * mod, int div)
        : Observador (mod, div) {}
     // Para pessoas idosas, se o nível de excitação
     // tem mais de 150, correm risco de ataque cardíaco
     atualização nula ()
     {
        bool marcado = getSubject () -> getScored ();
        setExcitementLevel (getExcitementLevel () + 1);
        if (score && getExcitementLevel ()> 150)
        {
          cout << "A equipe do velho observador marcou !!"
               << "Seu nível de excitação é"
               << getExcitementLevel ()
               << "cuidado com ataques cardíacos!" << endl;
        }outro{
          cout << "O time não marcou. Yeeeih não há com o que se preocupar"
               << endl;
        }
    } // fim da atualização ()
};
classe Young_ConcreteObserver: public Observer
{
   público:
     // Chama o construtor pai para registrar com o assunto
     Young_ConcreteObserver (Assunto * mod, int div)
       : Observador (mod, div) {}
     // Para pessoas idosas, se o nível de excitação
     // é superior a 100 eles correm risco de ataque cardíaco
     atualização nula ()
     {
        bool marcado = getSubject () -> getScored ();
        setExcitementLevel (getExcitementLevel () + 1);
        if (score && getExcitementLevel ()> 100)
        {
          cout << "A equipe do jovem observador marcou !!"
               << "Seu nível de excitação é"
               << getExcitementLevel ()
               << "não beba e dirija !!" << endl;
        }outro{
          cout << "O time não marcou. Yeeh não há com que se preocupar"
               << endl;
       }
    } // fim da atualização ()
};

Função principal
Os observadores concretos se registram na instância Object. O estado deles é o nível de excitação, que é o segundo parâmetro. Quando o evento é acionado “subj.setScored (true)”, o Object :: notify () é chamado para atualizar os observadores registrados. No cenário abaixo, temos três observadores, o youngObs1 é superexcitado e corre o risco de beber e dirigir, o oldObs1 também superexcitado e corre um risco diferente (de ataque cardíaco). Por fim, youngObs2, que também é jovem como o primeiro, não tem com o que se preocupar, pois não é superexcitado.

É importante notar que os três observadores foram atualizados independentemente com base em seu estado (nível de excitação) e seu tipo (jovem ou velho).
int main () {
   Sujeito sujeito;
   Young_ConcreteObserver youngObs1 (& subj, 100);
   Old_ConcreteObserver oldObs1 (& subj, 150);
   Young_ConcreteObserver youngObs2 (& subj, 52);
   subj.setScored (true);
}
// Saída
// A equipe do Young Observer marcou !! O nível de emoção dele é 101
// não beba e dirija !!
// A equipe do Old Observer marcou !! Seu nível de emoção é 151 assistir
// fora de ataques cardíacos! O time não marcou.
// Yeeh nada para se preocupar

Existem alguns benefícios para o uso do padrão Observer e alguns pontos a serem observados quando esse padrão deve ser abordado [Learning Python Design Patterns].

  • O padrão Observador fornece um design em que o Assunto e o Observador são fracamente acoplados. O assunto não precisa saber sobre a classe ConcreteObserver. Qualquer novo Observador pode ser adicionado a qualquer momento. Não há necessidade de modificar o Assunto quando um novo Observador é adicionado. Observadores e sujeitos não estão vinculados e são independentes um do outro, portanto, as mudanças no Assunto ou Observador não afetarão um ao outro.
  • Não há opção para composição, pois a interface do Observer pode ser instanciada.
  • Se o Observador for mal utilizado, ele pode facilmente adicionar complexidade e levar a problemas de desempenho.
  • As notificações podem não ser confiáveis ​​e podem resultar em condições de corrida ou inconsistência.

O próximo blog será um guia rápido para o padrão de design do Bridge. É um padrão de design estrutural que é bastante utilizado na indústria. Não se esqueça de curtir / bater palmas na postagem do meu blog e seguir minha conta. Isso é para me dar a satisfação de ter ajudado alguns colegas desenvolvedores e me pressionado a continuar escrevendo. Se você deseja conhecer um padrão de design específico, informe-me para que eu possa fornecê-lo no futuro.

Outras guias rápidas sobre padrões de design:

  1. Design Patterns - Um guia rápido para Abstract Factory.
  2. Design Patterns - Um guia rápido para o Bridge Pattern.
  3. Padrões de design - Um guia rápido para o Builder Pattern.
  4. Design Patterns - Um guia rápido para Decorator Pattern.
  5. Design Patterns - Um guia rápido para Facade Pattern.
  6. Design Patterns - Um guia rápido para o Observer Pattern.
  7. Design Patterns - Um guia rápido para Singleton Pattern.