Desenvolvimento - C#

Observer - Padrão de Projeto com Microsoft .NET C#

Veja neste artigo uma abordagem sobre o padrão de projeto observer. Entenda a definição, responsabilidades e importância desse padrão nos sistemas orientado a objeto.

por Flávio Secchieri Mariotti



Observer - Padrão de Projeto com Microsoft

Introdução

Padrão de Projeto ou, em inglês, Design Patterns é uma forma organizada de criar soluções reutilizáveis para os problemas recorrentes do dia a dia de um projetista ou programador.

Por que usar Padrão de Projeto?

É possível desenvolver software sem utilizar padrões de projetos e o fato de usar não caracteriza um código ou software de qualidade. Padrão de projeto é somente um dos vários detalhes que compõem o desenvolvimento de um software. Usar padrões de projetos é uma escolha que cabe a cada desenvolvedor/time. Existem casos nos quais as empresas desenvolvem seus próprios padrões e suas frameworks de trabalho.

O conceito padrão de projeto foi criado na década de 70 pelo arquiteto e matemático Christopher Alexander, um australiano que foi um dos principais críticos da arquitetura moderna. Alexander definiu dois pontos fundamentais para criação de um padrão, apontando as características e formato básico que um "objeto" deve conter para ser classificado como padrão:

Características

· Encapsulamento

· Generalidade

· Equilíbrio

· Abstração

· Abertura

· Combinatoriedade

Formato

· Nome

· Exemplo

· Contexto

· Problema

· Solução

Embora o que Christopher Alexander estivesse propondo fossem padrões para construções civis, é nítida a relação com o desenvolvimento de software. Portanto, podemos compreender o que é necessário para criação de um padrão.

Com isso, vamos então entender quais os benefícios e vantagens que, ao desenvolver software utilizando padrões de projetos, o time de desenvolvimento pode obter. Alguns acontecimentos são comuns no dia a dia das fábricas de software, tais como: giro de funcionários, mudanças no sistema em produção, correção de erros, implementação de novos recursos, customizações especificas e etc. Os padrões de projeto tem como objetivo facilitar o desenvolvimento de software com soluções reutilizáveis.

Observer - Padrão de Projeto

Este padrão de projeto se aplica para definir dependências um para vários (1-*), logo, quando um objeto tem alteração de estado, todos os seus dependentes são notificados e atualizados automaticamente. Observer também é conhecido como Dependents, Publish-Subscribe.

Classificação

Padrões de projeto descrevem 23 modelos de desenho, porém, cabe ressaltar que existem centenas de padrões. No famoso livro Design Patterns dos autores Gamma, Helm, Johnson e Vlissides é apresentado 23 padrões de projeto, documentados e já conhecidos no mundo do desenvolvimento de software. É importante lembrar que isso não significa que esses padrões são os mais úteis para se implementar, sendo assim, vale a pena pesquisar padrões em outras fontes.

Estes 23 padrões são divididos em padrões de criação, padrões estruturais e padrões comportamentais. Vamos explorar todos eles na série de artigos sobre o assunto. O padrão de projeto Observer está classificado como padrões comportamentais.

Quando devo aplicar o padrão Observer?

Um desafio comum enfrentado por arquitetos e desenvolvedores de software é a dificuldade em manter uma coleção de classes cooperantes com consistência entre os objetos relacionados quando existe a necessidade de executar o particionamento de um sistema.

Para não reduzir a reusabilidade das classes, é importante a busca por alternativas que não consistem em tornar as classes fortemente acopladas. Logo, o padrão observer descreve como estabelecer esses relacionamentos.

O uso do padrão observer se aplica em situações como:

· quando uma abstração contém dois sentidos e um dependente do outro, pode-se encapsular esses aspectos em objetos separados, com isso, permite-se a reutilização independente dos objetos;

· quando um objeto necessita ter a habilidade de notificar outros objetos sem fazer hipóteses e sem usar informações, sobre quem são esses objetos. Portanto, evitar que esses objetos sejam fortemente acoplados.

 

Observer com Microsoft C#.NET

O framework Microsoft .NET C#, oferece suporte com duas interfaces para o uso do padrão observer, são elas: IObserver e IObservable. Como o principal foco deste artigo é mostrar a responsabilidade do padrão observer e como ele funciona, não vamos utilizar essas interfaces e, sim, criar nossa própria com o intuito de deixar claro seu comportamento.

Para conhecer mais detalhes sobre as interfaces implementadas na framework do Microsoft C#, acesse os links abaixo:

· IObserver: http://msdn.microsoft.com/en-ca/library/dd783449.aspx

· IObservable: http://msdn.microsoft.com/en-ca/library/dd990377.aspx

Estrutura

O padrão observer apresenta diversas variações, porém existe uma premissa básica do padrão que envolve dois objetos: sujeito e observador, conforme representado na figura 1. Para compreendermos melhor o que isso significa, vamos contextualizá-los para programação. O observador é o objeto responsável pela exibição de dados para o usuário, logo, o sujeito representa uma abstração do negócio que é criado com base no requisito do sistema. Com isso, quando ocorre uma alteração no objeto assunto, o observador recebe a atualização automática. 

Figure 1. Modelo representativo do padrão observer

Para a implementação desse modelo em código fonte, a estrutura básica do padrão irá conter quatro objetos, conforme apresentado na figura 2.

Figure 2. Diagrama representativo da estrutura básica do padrão observer

Estrutura do exemplo - Visualizador de aplicações financeiras com observer

Neste artigo vamos desenvolver um projeto que apresenta diferentes telas de um aplicativo para investimentos na bolsa de valores. O intuito é demonstrar como se implementa o padrão observer em um sistema real. Deste modo, o aplicativo não contém as reais funcionalidades de um Home Broker e, sim, conceitos básicos das telas de apresentações das aplicações financeiras.

Nossa estrutura irá ficar conforme a figura 3 abaixo:

Figure 3 Diagrama do projeto visualizador de aplicações financeiras com observer

Código do exemplo - Visualizador de aplicações financeiras com observer

    class Program

    {

        static void Main(string[] args)

        {

            AplicacaoFinanceira aplicacaoFinanceira = new AplicacaoFinanceira();

            MostrarMinhaListaFinanceira mostraMinhaListaFinanceira = new MostrarMinhaListaFinanceira(aplicacaoFinanceira);

            MostrarMinhaTelaEstatisticas mostraMinhaListaEstatisticas = new MostrarMinhaTelaEstatisticas(aplicacaoFinanceira);

            // Incluir quantas classes/telas forem necessárias...

            aplicacaoFinanceira.setMudancasNaBolsa("Microsoft", 28.15f, 28.28f, 28.05f, 16153.384f);

            aplicacaoFinanceira.setMudancasNaBolsa("Hewllet-Packard", 27.02f, 27.05f, 26.88f, 2637.768f);

            aplicacaoFinanceira.setMudancasNaBolsa("Google Inc.", 640.97f, 640.99f, 631.46f, 866.907f);

        }

    }

    //   Esta etapa vamos criar as 3 interfaces

    // para implementãção do padrão observer.

    public interface Sujeito

    {

        void registrarObservador(Observer o);

        void removerObservador(Observer o);

        void notificarObservador();

    }

    public interface Observer

    {

        void atualizar(string empresa, float abrir, float alto, float baixo, float volume);

    }

    public interface MostrarAplicacoes

    {

        void Mostrar();

    }

    // A classe AplicacaoFinanceira representa a classe concreta do sujeito.

    class AplicacaoFinanceira:Sujeito

    {

        private List<Observer> observadores;

        private string empresa;

        private float abrir;

        private float alto;

        private float baixo;

        private float volume;

        public AplicacaoFinanceira()

        {

            observadores = new List<Observer>();

        }

        public void registrarObservador(Observer observer)

        {

            observadores.Add(observer);

        }

        public void removerObservador(Observer observer)

        {

            int index = observadores.IndexOf(observer);

            if (index >= 0) observadores.RemoveAt(index);

        }

        public void notificarObservador()

        {

            for (int index = 0; index < observadores.Count; index++)

            {

                Observer observer = observadores[index];

                observer.atualizar(empresa, abrir, alto, baixo, volume);

            }

        }

        public void AlteracaoNaBolsaDeValores()

        {

            notificarObservador();

        }

        public void setMudancasNaBolsa(string empresa, float abrir, float alto, float baixo, float volume)

        {

            this.empresa = empresa;

            this.abrir = abrir;

            this.alto = alto;

            this.baixo = baixo;

            this.volume = volume;

            AlteracaoNaBolsaDeValores();

        }

    }

    // A classe MostrarMinhaListaFinanceira representa a classe concreta do observer.

    public class MostrarMinhaListaFinanceira : Observer, MostrarAplicacoes

    {

        private string empresa;

        private float abrir;

        private float alto;

        private Sujeito aplicacaoFinanceira;

        public MostrarMinhaListaFinanceira(Sujeito aplicacaoFinanceira)

        {

            this.aplicacaoFinanceira = aplicacaoFinanceira;

            aplicacaoFinanceira.registrarObservador(this);

        }

        public void atualizar(string empresa, float abrir, float alto, float baixo, float volume)

        {

            this.empresa = empresa;

            this.abrir = abrir;

            this.alto = alto;

            Mostrar();

        }

        public void Mostrar()

        {

            Console.WriteLine("Empresa: " + empresa.ToString());

            Console.WriteLine("Inicia: " + abrir.ToString());

            Console.WriteLine("Previsão máxima: " + alto.ToString());

            Console.WriteLine("");

        }

    }

    // A classe MostrarMinhaTelaEstatistica também representa a classe concreta do observer.

    // vale lembrar que posso e devo criar quantas classes observadoras forem necessários.

    public class MostrarMinhaTelaEstatisticas : Observer, MostrarAplicacoes

    {

        private string empresa;

        private float abrir;

        private float alto;

        private float baixo;

        private float volume;

        private Sujeito aplicacaoFinanceira;

        public MostrarMinhaTelaEstatisticas(Sujeito aplicacaoFinanceira)

        {

            this.aplicacaoFinanceira = aplicacaoFinanceira;

            aplicacaoFinanceira.registrarObservador(this);

        }

        public void atualizar(string empresa, float abrir, float alto, float baixo, float volume)

        {

            this.empresa = empresa;

            this.abrir = abrir;

            this.alto = alto;

            this.baixo = baixo;

            this.volume = volume;

            Mostrar();

        }

        public void Mostrar()

        {

            Console.WriteLine("Empresa: " + empresa.ToString());

            Console.WriteLine("Percentual valor baixo para alto : " + PercentualBaixoRelacaoAlto(alto, baixo));

            Console.WriteLine("Volume: " + volume.ToString());

            Console.WriteLine("");

        }

        public string PercentualBaixoRelacaoAlto(float alto, float baixo)

        {

            float resultado = 0;

            if ((baixo != 0f) && (alto != 0f)) resultado = (((baixo / alto)*100)-100);

            return resultado.ToString();

        }

    }

Conclusão

Aprendemos, então, com esse artigo, o principal objetivo do padrão observer e como podemos implementá-lo. Também aprendemos que o padrão observer é o que define uma relação um-para-muitos entre os objetos. Com isso, fica claro que o "Sujeito", designado no C# como Observable, tem a responsabilidade de atualizar os observadores usando uma interface comum e que os "Observadores" são levemente ligados ao objeto sujeito, já que o Observable não precisa saber nada sobre ele, exceto, obviamente, que eles implementam a interface Observer.

Finalizando, com mais este artigo da série padrões de projeto, podemos dizer com segurança que, se utilizados com eficácia, os padrões são ferramentas poderosas no desenvolvimento de sistemas flexíveis.

Comunicado Importante

Espero que tenham gostado do conteúdo explorado neste artigo. Caso exista qualquer dúvida relacionada ao conteúdo ou interesse em conhecer um pouco mais sobre padrão de projeto observer, informe seu interesse usando o recurso de "comentários".

Referência

Steven John Metsker. Design Patterns in C#, Addison-Wesley Professional, 2004.

Erich Gramma; Richard Helm; Ralph Johnson; John Vlissides. Design Patterns Elements

of Reusable Object-Oriented Software, Addison-Wesley Professional, 1994.

Flávio Secchieri Mariotti

Flávio Secchieri Mariotti - Especialista em Engenharia e Arquitetura de Software. Pós Graduado pelo Instituto de Pesquisa Avançada de Tecnologia IBTA em Engenharia de Software baseado em SOA. Bacharel em Sistemas de Informação pela UNIUBE e técnico em Processamento de Dados pela FEB. Consultor independente no desenvolvimento de software em arquitetura OO, SOA, GIS e Plataforma .NET.