Desenvolvedores.Net - TechBlog

Tag Archives: Object-Oriented

LSP Liskov Subtitution Principle (Princípio da Substituição de Liskov)

0
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (Sem votação.)
Loading...
14 de agosto de 2017

Veja o índice completo do tópico “S.O.L.I.D”

Olá pessoas …

Continuando a nossa saga de artigos, vamos agora ver a letra do acróstico que define a sigla LSP Liskov Substitution Principle (Princípio da Substituição de Liskov).

Definido o LSP

Não tem como falar deste princípio sem saber quem o criou, Barbara Liskov, em 1988.

A definição mais usada, e a mais simples, é:

note-taking Classes derivadas podem ser substituídas por suas classes de base.

Se q(x) é uma propriedade demonstrável dos objetos x de tipo T. Então q(y) deve ser verdadeiro para objetos y de tipo S onde S é um subtipo de T.

Portanto, a visão de “subtipo” defendida por Liskov é baseada na noção da substituição; isto é, se S é um subtipo de T, então os objetos do tipo T, em um programa, podem ser substituídos pelos objetos de tipo S sem que seja necessário alterar as propriedades deste programa.

(fonte: Wikipédia)

 

certo-ou-errado

Certo ou errado?

E lá vamos nós novamente ver como fica o princípio ferido, os problemas que nos trazem e a solução do mesmo …

 Ferindo o princípio

errado

Isto está errado.

Utilitário de atualização de saldos.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Errado.LSP.Abstract;

namespace Solid.Errado.LSP
{
    public static class AtualizarSaldo
    {
        #region Public Methods

        public static void AtualizarSaldos(params AtualizaSaldoBase[] saldos)
        {
            foreach(var saldo in saldos)
            {
                if(saldo is AtualizarSaldoAnual)
                    ((AtualizarSaldoAnual)saldo).AtualizarAno();
                else if(saldo is AtualizarSaldoMensal)
                    ((AtualizarSaldoMensal)saldo).AtualizarMes();
                else if(saldo is AtualizarSaldoDiario)
                    ((AtualizarSaldoDiario)saldo).AtualizarDia();
            }
        }

        #endregion Public Methods
    }
}

Abstração para as classes de saldo concretas

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

namespace Solid.Errado.LSP.Abstract
{
    public abstract class AtualizaSaldoBase
    {
        #region Public Properties

        public double SaldoAtual { get; set; }

        #endregion Public Properties
    }
}

Cálculo de saldo diário

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Errado.LSP.Abstract;
using System;

namespace Solid.Errado.LSP
{
    public class AtualizarSaldoDiario: AtualizaSaldoBase
    {
        #region Public Methods

        public void AtualizarDia()
        {
            Console.WriteLine("O saldo DIARIO foi atualizado.");
        }

        #endregion Public Methods
    }
}

Cálculo de saldo mensal

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Errado.LSP.Abstract;
using System;

namespace Solid.Errado.LSP
{
    public class AtualizarSaldoMensal: AtualizaSaldoBase
    {
        #region Public Methods

        public void AtualizarMes()
        {
            Console.WriteLine("O saldo MENSAL foi atualizado.");
        }

        #endregion Public Methods
    }
}

Cálculo de saldo anual

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Errado.LSP.Abstract;
using System;

namespace Solid.Errado.LSP
{
    public class AtualizarSaldoAnual: AtualizaSaldoBase
    {
        #region Public Methods

        public void AtualizarAno()
        {
            Console.WriteLine("O saldo ANUAL foi atualizado.");
        }

        #endregion Public Methods
    }
}

Problemas ao ferir o princípio

  • Primeiro, já matamos o polimorfismo. Na classe de atualização de saldos “public static class AtualizarSaldo” no método “public static void AtualizarSaldos(params AtualizaSaldoBase[] saldos)” temos que fazer “if/else” para determinar que tipo de classe iremos usar para calcular o saldo.
  • Na mesma classe citada acima, mesmo que utilizado a herança nas demais classes de atualização de saldo, eu não consigo o reaproveitamento do código de atualização de saldos, uma vez que eu tenho que identificar qual o tipo de classe e determinar o método que é chamado, veja no fragmento abaixo o “if/else”.
if(saldo is AtualizarSaldoAnual)
    ((AtualizarSaldoAnual)saldo).AtualizarAno();
else if(saldo is AtualizarSaldoMensal)
    ((AtualizarSaldoMensal)saldo).AtualizarMes();
else if(saldo is AtualizarSaldoDiario)
    ((AtualizarSaldoDiario)saldo).AtualizarDia();
  • Violação do princípio OCP
  • Dar manutenção neste código é ter dores de cabeça, uma vez que teremos que lembrar de alterar este método de cálculo de saldos sempre que um novo tipo de saldo for criado.

Corrigindo o princípio

Como o LSP anda de mãos dadas com o OCP, podemos perceber que as classes de saldos definidos no princípio OCP atendem 100% os requisitos deste princípio . Vamos relembrar estas classes:

certo

Isto está correto.

Veja as classes que foram criadas:

Abstração para os saldos, desta forma eu posso herdar minha classe principal e modificar apenas  o que eu preciso.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

namespace Solid.Certo.Utility.Abstract
{
    public abstract class AtualizaSaldoBase
    {
        #region Public Methods

        public abstract void Atualizar();

        #endregion Public Methods
    }
}

Classe concreta de atualização de saldos diários.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Certo.Utility.Abstract;
using System;

namespace Solid.Certo.Utility
{
    public class AtualizaSaldoDiario: AtualizaSaldoBase
    {
        #region Public Methods

        public override void Atualizar()
        {
            Console.WriteLine("O saldo DIÁRIO foi atualizado.");
        }

        #endregion Public Methods
    }
}

Classe concreta de atualização de saldos mensais.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Certo.Utility.Abstract;
using System;

namespace Solid.Certo.Utility
{
    public class AtualizaSaldoMensal: AtualizaSaldoBase
    {
        #region Public Methods

        public override void Atualizar()
        {
            Console.WriteLine("O saldo MENSAL foi atualizado.");
        }

        #endregion Public Methods
    }
}

Classe concreta de atualização de saldos anuais.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Certo.Utility.Abstract;
using System;

namespace Solid.Certo.Utility
{
    public class AtualizaSaldoAnual: AtualizaSaldoBase
    {
        #region Public Methods

        public override void Atualizar()
        {
            Console.WriteLine("O saldo ANUAL foi atualizado.");
        }

        #endregion Public Methods
    }
}

Classe de serviço para atualização de saldos no estoque.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Certo.Utility.Abstract;
using System.Collections.Generic;

namespace Solid.Certo.Service
{
    public static class AtualizaSaldoService
    {
        #region Public Methods

        public static void AtualizarSaldos(List<AtualizaSaldoBase> saldos)
        {
            foreach(AtualizaSaldoBase saldo in saldos)
            {
                saldo.Atualizar();
            }
        }

        #endregion Public Methods
    }
}

Analisando as classes definidas acima, podemos perceber que:

  • As classes de saldo herdam diretamente da classe “public abstract class AtualizaSaldoBase”;
  • Passamos a ter acesso ao método “Atualizar()”, desta forma eu consigo chamar o método “Atualizar()” a partir da classe mais genérica (Veja: Generalização e Especialização), é utilizado o polimorfismo para evitar o uso de “if/ else”, como podemos ver no fragmento abaixo da classe de serviço de atualização de saldo.
public static class AtualizaSaldoService
{
	#region Public Methods

	public static void AtualizarSaldos(List<AtualizaSaldoBase> saldos)
	{
		foreach(AtualizaSaldoBase saldo in saldos)
		{
			saldo.Atualizar();
		}
	}

	#endregion Public Methods
}
  • Se necessário criar um novo tipo de saldo, basta criar a classe, herdar de “AtualizaSaldoBase” e implementar a chamada onde queremos que o novo saldo seja calculado sem termos que nos preocupar em fazer “if” para identificar o tipo de  saldo que iremos calcular;
  • A classe mais especializada pode ser facilmente convertida na classe mais genérica e atende ao princípio em questão;

Pegadinha e cuidados

Que diabos O patinho feio; história infantil; está fazendo aqui?

Ora! É simples. Se nada como um pato, tem as habilidades de um pato, come igual ao pato mas é um marreco cisne, então você tem um problema de abstração.

Vamos ao exemplo mais clássico de erro de abstração Quadrado x Retângulo. Não poderíamos falar de LSP sem comentar sobre o Quadrado x Retângulo.

Todo quadrado é também um retângulo, pois ambos tem ângulos retos. Mas nem todo retângulo é um quadrado. Quem me disse foi o Professor Procópio do Matemática Rio, no vídeo Qual a Diferença entre Retângulo e Quadrado?

Vamos aos fontes:

Classe de retângulo.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

namespace Solid.Errado.QuadradoRetangulo
{
    public class Retangulo
    {
        #region Public Properties

        public virtual int Altura { get; set; }
        public virtual int Largura { get; set; }

        #endregion Public Properties
    }
}

Classe do quadrado que herda de retângulo (Já falei sobre herança?)

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

namespace Solid.Errado.QuadradoRetangulo
{
    public class Quadrado: Retangulo
    {
    }
}

Utilitário para cálculo de área.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Errado.QuadradoRetangulo;

namespace Solid.Errado.Utility
{
    public static class CalculaArea
    {
        #region Public Methods

        //Sim, este método poderia ser da classe retângulo.
        //Mas para efeito de exemplo, vou deixar ele aqui.
        public static int CalcularArea(Retangulo retangulo)
        {
            return retangulo.Altura * retangulo.Largura;
        }

        #endregion Public Methods
    }
}

Chamada de exemplo para ambos os casos

int altura;
int largura;

Console.WriteLine("\r\nInforme a altura:");
altura = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Informe a largura:");
largura = Convert.ToInt32(Console.ReadLine());

//aqui, podemos criar um quadrado normalmente.
Quadrado quadrado = new Quadrado
{
    Altura = altura,
    Largura = largura
};

//aqui, criamos um retângulo com base em um quadrado, atendendo ao princípio do LSP
Retangulo retanguloQuadrado = new Quadrado
{
    Altura = altura,
    Largura = largura
};

//Aqui criamos um retângulo normalmente.
Retangulo retangulo = new Retangulo
{
    Altura = altura,
    Largura = largura
};

int quadradoAreaEsperada = altura * altura;
int retanguloAreaEsperada = largura * altura;
//o método CalcularArea() espera um retângulo, perceba que podemos passar tanto um quadrado quanto um retângulo. Atendendo ao princípio LSP.
Console.WriteLine($"A área correta do retângulo é {retanguloAreaEsperada} e a calculada foi {Errado.Utility.CalculaArea.CalcularArea(retangulo)}");
Console.WriteLine($"A área correta do quadrado  é {quadradoAreaEsperada} e a calculada foi {Errado.Utility.CalculaArea.CalcularArea(quadrado)}");   

Vamos analisar os fontes e explicar o real problema de não tomarmos cuidado no momento de definirmos nossas abstrações.

As classes definidas, a primeira vista, não ferem o princípio LSP, uma vez que podem ser herdadas, terem seus métodos sobrescritos, são reutilizáveis e eu posso criar a mais genérica pela classe mais especializada, posso utilizar a classe mais genérica como parâmetro e passar a mais especializada, como diz o princípio da substituição.
No caso, como foi mostrado, eu posso criar um retângulo a partir de um quadrado. Veja o fragmento abaixo

//aqui, criamos um retângulo com base em um quadrado, atendendo ao princípio do LSP
Retangulo retanguloQuadrado = new Quadrado
{
    Altura = altura,
    Largura = largura
};

Mas isso, tem um erro. Imagina que nossos sistema é usado para calcular as esquadrias em um prédio. E neste sistema eu preciso calcular a área para pedir que se corte uma esquadria quadrada. considerando que o quadrado tem seus lados iguais, e meu sistema aceita que eu passe altura x largura diferentes, mesmo para a classe “Quadrado”, eu tenho um erro de regra de negócio e de abstração.

Se eu crio uma classe retângulo a partir de uma classe “Quadrado” a área calculada ficará errada se eu passar os valores de “Altura” e “Largura” diferentes.

//aqui, criamos um retângulo com base em um quadrado, atendendo ao princípio do LSP
Retangulo retanguloQuadrado = new Quadrado
{
    Altura = 10,
    Largura = 5
};

//aqui acontece o erro, uma vez que eu passei 10x5=50. O meu calculo de área deveria considerar Altura X Largura igual para o calculo de quadrados.
Console.WriteLine($"A área correta do quadrado  é {Errado.Utility.CalculaArea.CalcularArea(retanguloQuadrado)}");

Pegando um exemplo real, nos artigos anteriores temos tratado as nossas classes de saldos como saldos de estoque. Imaginem que eu queira calcular o saldo financeiro. Em sua essência o cálculo é igual um menos o outro, entradas menos saídas. Só que de um lado eu estou falando de produtos, e de outro de valores monetários, cada qual com suas regras específicas.

Para terminar

Para atender ao princípio de Liskov, temos que garantir que as nossa classes genéricas, sejam substituíveis pelas nossas classes especializadas; Veja: Generalização x Especialização; Desta forma atendemos também o OCP.

Cuidado ao usar o princípio “É UM”, e preste muita atenção que este princípio se aplica ao comportamento da classe e não aos tipos ou subtipos. Valorize o polimorfismo.

SaberMais

Para saber mais.

O fonte utilizado aqui pode ser baixado pelo GITHub em https://github.com/desenvolvedores-net/ArtigoSOLID


É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

OCP Open/ Closed Principle (Princípio aberto/fechado)

0
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (1 votos, média: 5,00 de 5)
Loading...
10 de agosto de 2017

Veja o índice completo do tópico “S.O.L.I.D”

Olá pessoas …

Continuando a nossa saga de artigos, vamos agora ver a letra O do acróstico, que define a sigla OCP Open/Closed Principle (Princípio aberto/ fechado).

Definindo OCP

 

 

Momento piadinha infame do artigo

Não iremos falar sobre o Robocop, mas toda vez que eu falo a sigla  OCP (Omni Consumer Products) eu vejo o Robocop na minha mente

 

 

 

Bom, vamos ao que interessa…

note-taking Você deve ser capaz de estender um comportamento de uma classe sem modificá-lo.

Esta declaração quer dizer que você tem que escrever suas classes de forma que elas possam ser herdadas e ter seu comportamento estendido ao invés de alterar o código existente. Para fazer isso, iremos usar o que chamamos de herança e abstração.

SaberMais Para saber mais.

Caso não esteja familiarizado com os termos herança e abstração, dê uma lida no artigo sobre “Herança”  e no artigo sobre “Abstração“.
Tenho certeza que suas dúvidas serão sanadas.

Problemas em ferir o princípio OCP

  • Reaproveitamento de código é praticamente nulo. CTRL+C/ CTRL+V, não é reaproveitamento de código;
  • As classes podem fazer partes de componentes espalhados pela aplicação;
  • O que acontece se precisarmos de uma nova implementação?
    • Colocamos mais um if/else if, um switch…case ?
    • Criamos uma nova classe e copiamos o código?
  • Recompilar e fazer deploy em todos os componentes impactados;

Espero que você tenha um bom caso de testes escrito para cada situação.

certo-ou-errado Certo ou errado?

Abaixo vou colocar os códigos “ERRADO” e “CERTO” e ao final, analisar cada um.

Ferindo o OCP

errado

Isto está errado.

Primeiro, vamos criar nossa classe de venda para atender as vendas da nossa aplicação.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using System;
using System.Collections.Generic;

namespace Solid.Errado
{
    public class Venda
    {
        #region Public Properties

        public int Codigo { get; set; }
        public int CodigoCliente { get; set; }
        public List<string> Itens { get; set; }
        public double TotalVenda { get; set; }

        #endregion Public Properties

        #region Public Methods

        public void Salvar()
        {
            Validar();
            Console.WriteLine("A venda foi salva.");
            CalcularTotal();
            AtualizarSaldo();
        }

        private void CalcularTotal()
        {
            Console.WriteLine("O total da venda foi calculado.");
        }

        public void Validar()
        {
            if(CodigoCliente == 0)
                throw new Exception("O código do cliente é obrigatório.");

            if((Itens?.Count ?? 0) == 0)
                throw new Exception("A quantidade de itens vendida não pode ser zero.");
        }

        public void AtualizarSaldo()
        {
            Utility.AtualizarSaldo.AtualizarSaldos(new AtualizarSaldoAnual(),
                                                   new AtualizarSaldoMensal(),
                                                   new AtualizarSaldoDiario());
        }
        #endregion Public Methods
    }
}

Com a classe de vendas criada, precisamos atualizar os saldos de estoque, pois a venda faz uma saída em nossos produtos.
Esta classe é um utilitário de atualização de saldos de estoque, e recebe objetos para realizar os cálculos de saldo de estoque.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

 namespace Solid.Errado.Utility
{
    public static class AtualizarSaldo
    {
        #region Public Methods

        public static void AtualizarSaldos(params object[] saldos)
        {
            foreach(var saldo in saldos)
            {
                if(saldo.GetType() == typeof(AtualizarSaldoAnual))
                    ((AtualizarSaldoAnual)saldo).Atualizar();
                else if(saldo.GetType() == typeof(AtualizarSaldoMensal))
                    ((AtualizarSaldoMensal)saldo).Atualizar();
                else if(saldo.GetType() == typeof(AtualizarSaldoDiario))
                    ((AtualizarSaldoDiario)saldo).Atualizar();
            }
        }

        #endregion Public Methods
    }
}

E abaixo, as classes de atualização de saldo diário, mensal e anual.

Saldo Diário

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using System;

namespace Solid.Errado
{
    public class AtualizarSaldoDiario
    {
        #region Public Methods

        public void Atualizar()
        {
            Console.WriteLine("O saldo DIARIO foi atualizado.");
        }

        #endregion Public Methods
    }
}

Saldo Mensal

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using System;

namespace Solid.Errado
{
    public class AtualizarSaldoMensal
    {
        #region Public Methods

        public void Atualizar()
        {
            Console.WriteLine("O saldo MENSAL foi atualizado.");
        }

        #endregion Public Methods
    }
}

Saldo Anual

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using System;

namespace Solid.Errado
{
    public class AtualizarSaldoAnual
    {
        #region Public Methods

        public void Atualizar()
        {
            Console.WriteLine("O saldo ANUAL foi atualizado.");
        }

        #endregion Public Methods
    }
}

Logo de cara já percebemos que a classe de vendas fere completamente o Princípio da Responsabilidade Única, ela se “valida”, ele atualiza os saldos de estoque entre outros problemas que você já é capaz de identificar com a leitura do primeiro artigo.

Mas o que realmente nos interessa aqui, são as classes de saldos. Estas ferem completamente o princípio OCP (Aberto/ Fechado).

  • Mesmo que eu faça a herança destas classes, por exemplo, para criar um saldo quinzenal, eu não consigo substituir o comportamento do método atualizar;
  • O utilitário de atualização de saldos, classe “Utility.AtualizarSaldo“, tem que conhecer as classes de saldos e fazer um “if” para identificar qual é o tipo de saldo que deverá ser atualizado;
    • Se eu precisar implementar mais um tipo de saldo, tenho que lembrar deste “if” e criar um método com o mesmo nome “Atualizar” para manter um mínimo de padrão;
    • Este utilitário aceita tipos “object“, isso quer dizer que se eu passar uma classe errada para o meu método, posso ter um erro em tempo de execução, ou não ter meus saldos de estoque calculados corretamente;
  • Percebemos que não houve reaproveitamento de código entre as classes de saldos;

Corrigindo o princípio

Neste momento, iremos corrigir o princípio e ao final analisar as modificações que foram realizadas para atender ao princípio do OCP.

certo

Isto está correto.

Veja as classes que foram criadas:

Classe de venda para realização das vendas em nosso estabelecimento.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Certo.Persistence.Abstract;
using Solid.Certo.Repository;
using System.Collections.Generic;

namespace Solid.Certo.Persistence
{
    public class Venda: EntityBase
    {
        #region Public Properties

        public int CodigoCliente { get; set; }
        public List<string> Itens { get; set; }
        public double TotalVenda { get; set; }

        #endregion Public Properties

        #region Public Methods

        public override void Salvar()
        {
            DBContext<Venda>.Salvar(this);
        }

        #endregion Public Methods
    }
}

Abstração para os saldos, desta forma eu posso herdar minha classe principal e modificar apenas  o que eu preciso.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

namespace Solid.Certo.Utility.Abstract
{
    public abstract class AtualizaSaldoBase
    {
        #region Public Methods

        public abstract void Atualizar();

        #endregion Public Methods
    }
}

Classe concreta de atualização de saldos diários.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Certo.Utility.Abstract;
using System;

namespace Solid.Certo.Utility
{
    public class AtualizaSaldoDiario: AtualizaSaldoBase
    {
        #region Public Methods

        public override void Atualizar()
        {
            Console.WriteLine("O saldo DIÁRIO foi atualizado.");
        }

        #endregion Public Methods
    }
}

Classe concreta de atualização de saldos mensais.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Certo.Utility.Abstract;
using System;

namespace Solid.Certo.Utility
{
    public class AtualizaSaldoMensal: AtualizaSaldoBase
    {
        #region Public Methods

        public override void Atualizar()
        {
            Console.WriteLine("O saldo MENSAL foi atualizado.");
        }

        #endregion Public Methods
    }
}

Classe concreta de atualização de saldos anuais.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Certo.Utility.Abstract;
using System;

namespace Solid.Certo.Utility
{
    public class AtualizaSaldoAnual: AtualizaSaldoBase
    {
        #region Public Methods

        public override void Atualizar()
        {
            Console.WriteLine("O saldo ANUAL foi atualizado.");
        }

        #endregion Public Methods
    }
}

Estratégia de venda, responsável pela nossa regra de negócio quando a venda for realizada.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Certo.Persistence;
using Solid.Certo.Strategy.Abstract;
using Solid.Certo.Utility.Abstract;
using System;
using System.Collections.Generic;

namespace Solid.Certo.Strategy
{
    public class VendaStrategy: PersistenceStrategyBase<Venda>
    {
        #region Public Methods

        public override void AntesDeSalvar(Venda entity)
        {
            base.AntesDeSalvar(entity);
            //vamos simular uma cálculo de total de venda
            Entidade.TotalVenda = 30;
            Console.WriteLine("O total da venda foi calculado");
        }

        public override IEnumerable<string> Validar()
        {
            if(Entidade.CodigoCliente == 0)
                yield return "O código do cliente é obrigatório.";

            if((Entidade.Itens?.Count ?? 0) == 0)
                yield return "A quantidade de itens vendida não pode ser zero.";

            yield break;
        }

        public override void DepoisDeSalvar(Venda entity)
        {
            base.DepoisDeSalvar(entity);

            Service.AtualizaSaldoService.AtualizarSaldos(new List<AtualizaSaldoBase>
            {
                new Utility.AtualizaSaldoDiario(),
                new Utility.AtualizaSaldoMensal(),
                new Utility.AtualizaSaldoAnual()
            });
        }

        #endregion Public Methods
    }
}

Classe de serviço para atualização de saldos no estoque.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Certo.Utility.Abstract;
using System.Collections.Generic;

namespace Solid.Certo.Service
{
    public static class AtualizaSaldoService
    {
        #region Public Methods

        public static void AtualizarSaldos(List<AtualizaSaldoBase> saldos)
        {
            foreach(AtualizaSaldoBase saldo in saldos)
            {
                saldo.Atualizar();
            }
        }

        #endregion Public Methods
    }
}

Como podemos ver, até a classe de vendas foi corrigida. Não fere mais  o primeiro princípio.

Como o foco foram as classes de saldos, iremos listar as vantagens desta abordagem.

  • Com o uso da abstração; (clique para saber mais sobre Abstração); conseguimos isolar os métodos comuns das nossas classes de cálculo de saldos;
  • Fizemos com que as classes filhas, implementassem o método “Atualizar” para que cada uma saiba como fazer a atualização de saldos em estoque;
  • As estratégias indicam o momento em que a atualização dos saldos devem ocorrer;
  • Se for necessário a inclusão do saldo quinzenal, basta apenas criar a classe e inserir na estratégia o momento em que se deseja atualizar o saldo;
  • Não é necessário fazer o uso de “if/else if” ou “switch…case” no momento da atualização de saldos, isso foi resolvido com o Polimorfismo;
    • Perceba que ao utilizar a abstração para chamar a atualização de  saldos, nos não precisamos mais nos preocupar em fazer “if/else if” e nem se preocupar se o tipo de objeto passado é do tipo saldo. Uma vez que a herança; (clique para saber mais sobre Herança) me permite fazer isto de forma transparente e garante que o tipo passado é do tipo saldo;
  • E se necessário fazer uma customização para determinado cliente, podemos usar e abusar do padrão “Strategy“.

O fonte utilizado aqui pode ser baixado pelo GITHub em https://github.com/desenvolvedores-net/ArtigoSOLID


É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

SRP Single Responsibility Principle (Princípio da Responsabilidade Única)

1
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (1 votos, média: 5,00 de 5)
Loading...
9 de agosto de 2017


Veja o índice completo do tópico “S.O.L.I.D”

Olá pessoas …

Dando início a nossa saga de artigos, vamos iniciar com a primeira letra do acróstico, S que define a sigla SRP Single Responsibility Principle (Princípio da Responsabilidade Única).

Definição

Isto quer dizer que nossas classes devem ter apenas uma, e somente uma, razão para ser modificada. Se a classe possuir mais de um motivo para ser modificada, a mesma não é coesa e isso já fere os princípios da POO (Programação Orientada à Objetos).

Problemas ao ferir o primeiro princípio.

  • Dificuldade de compreensão, logo dificuldade de manutenção na classe;
  • Não permite reuso;
  • Alto acoplamento, a classe depende de conhecer outras e outras e mais outras classes para poder funcionar, o que dificulta a manutenção ao alterar as classes de dependência;

Imaginem um canivete suíço, para quem não sabe, é este ai abaixo, o pai dos canivetes suíços.

Canivete criado por John S. Holler por volta de 1880, na Alemanha e possui 100 funções.
As tampinhas só foram inventadas em 1891. 
Fonte: Google

Este canivete, se fosse uma aplicação, fere completamente o primeiro princípio. O da responsabilidade única. Em nossas aplicações cada objeto deve fazer apenas o que ele se propõe a fazer.

A ostra ostreia, o gato gateia, o vento venta
(Prof. Clóvis Barros Filho)

Nossas classes devem seguir esta linha de pensamento, se é uma classe de serialização, ela serializa, seja em banco de dados, arquivos, memória. Não importa, ela apenas serializa.
Se nossa classe é de validação, ela apenas valida.

Certo ou Errado?

Nos exemplos abaixo eu sempre colocarei o certo e o errado para compararmos as classes e as soluções propostas. Tentarei usar problemas e soluções do dia-a-dia.

Antes de começarmos vamos ver a imagem abaixo:

Como podemos ver, é muito mais simples criar da forma errada. Mas te trará problemas de manutenção de código, de reusabilidade de código, seu código será macarrônico e anêmico. Alto Acoplamento e baixa coesão. No momento da manutenção deste código, erros simples se tornam complexos de serem resolvidos.

Como ferir o princípio.

Veja o exemplo de código abaixo:

errado

Isto está errado.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

 using System;
using System.Collections.Generic;

namespace Solid.Errado
{
    public class Cliente
    {
        #region Private Methods

        private void Adicionar()
        {
            Validar();
            Console.WriteLine($"O cliente {Nome} foi inserido.");
        }

        private void Atualizar()
        {
            Validar();
            Console.WriteLine($"O cliente {Nome} foi atualizado.");
        }

        #endregion Private Methods

        #region Public Properties

        public string CNPJ { get; set; }
        public int Codigo { get; set; }
        public DateTime DataCadastro { get; set; }
        public string Email { get; set; }
        public string Endereco { get; set; }
        public string Nome { get; set; }

        #endregion Public Properties

        #region Public Methods

        public void EnviarEmail()
        {
            Console.WriteLine($"O e-mail foi enviado para o endereço {Email}");
        }

        public void Excluir(long id)
        {
            Console.WriteLine($"O cliente {Nome} foi excluído.");
        }

        public void Salvar()
        {
            if(Codigo != 0)
                Atualizar();
            else
                Adicionar();

            EnviarEmail();
        }

        public IList<Cliente> Selecionar(long? id = null)
        {
            List<Cliente> result = new List<Cliente>();

            return result;
        }

        public bool Validar()
        {
            if(String.IsNullOrWhiteSpace(Nome))
                throw new Exception("O nome é obrigatório");

            if(!Email.Contains("@"))
                throw new Exception("O e-mail não é válido");

            if(CNPJ.Length != 14)
                throw new Exception("O CNPJ não é válido");

            return true;
        }

        #endregion Public Methods
    }
}

A classe cliente escrita acima, tem diversos pontos que ferem o princípio da Responsabilidade Única:

  • Ela é uma classe que se faz sua própria serialização, como pode ver na linha 17 do método “Adicionar()”, linha 23 do método “Atualizar()” e assim nos demais métodos. Logo esta classe tem que conhecer a implementação de sua forma de serialização, seja banco de dados, XML, JSON, memória;
  • Esta classe é responsável por enviar um e-mail ao cliente, assim que o mesmo é cadastrado na aplicação. Imagina se você criar outras classes de pessoas, como Fornecedor, Usuário, Fabricante, e se fizer necessário o envio de e-mail ao se criar cada pessoa. Cada classe terá sua implementação do método “EnviarEmail()” e você terá que dar manutenção em cada método das classes de pessoas, se por um acaso o servidor for modificado, por exemplo;
  • Esta classe é  responsável por validar seus próprios dados. Em uma customização para clientes, esta abordagem seria trabalhosa, uma vez que cada cliente pode ter sua forma de validação de dados;

Como resolver o problema.

certo

Isto está correto.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Certo.Persistence.Abstract;
using Solid.Certo.Repository;
using System;

namespace Solid.Certo.Persistence
{
    public class Cliente: EntityBase
    {
        #region Public Properties

        public string CNPJ { get; set; }
        public DateTime DataCadastro { get; set; }
        public string Email { get; set; }
        public string Endereco { get; set; }
        public string Nome { get; set; }

        #endregion Public Properties

        #region Public Methods

        public void Excluir()
        {
            DBContext<Cliente>.Excluir(Codigo);
        }

        public void Salvar()
        {
            DBContext<Cliente>.Salvar(this);
        }

        #endregion Public Methods
    }
}

Como podem ver:

  • Esta classe não se “Valida”;
    • Para resolver este problema utilizamos o padrão de projeto “Strategy“. Que são:
      • StrategyDependencyManager: Responsável por fazer a injeção de dependência das estratégias na classe cliente;
      • IStrategy: Define o contrato para qualquer estratégia que não seja de persistência;
      • IPersistenceStrategy: Define o contrato para as estratégias de persistência dos dados;
      • StrategyBase: Abstração de estratégias para os tipos mais básicos;
      • PersistenceStrategyBase: Abstração para estratégias de persistências;
      • ClienteStrategy: Estratégia concreta para validação de clientes;
    • A validação do CNPJ passou a ser de responsabilidade do padrão Validator (Visitor Pattern).
      • IValidator: Contrato para classes de validação de tipos;
      • CNPJValidator: Classe concreta para validação do tipo CNPJ;
        • Estes tópicos, Visitor Pattern e Validators serão discutidos em outros artigos. Aqui iremos falar apenas do conceito S.O.L.I.D.
  • Ela não conhece a implementação de serialização, no caso, podemos perceber que seria em um banco de dados;
    • Este problema foi resolvido usando a classe DbContext, mas temos que tomar cuidado com esta abordagem, pois depende do framework de acesso à dados que você vai utilizar, o mais comum, para desenvolvimento .NET é o Entity Framework. Este link do StackOverflow; em Português; tem uma boa explicação sobre com prós e contras;
  • Ela não manda e-mails;
    • Quem decide se o e-mail deve ou não ser enviado é a estratégia de cada cliente, para isso a estratégia utiliza-se do serviço de e-mail para o envio do e-mail para o cliente.
      • EmailService: Classe de serviço de e-mail responsável por enviar e-mail e receber e-mail em toda a aplicação;

Como podemos ver, esta classe apenas faz o que lhe diz respeito. Manter os dados dos clientes.

Não se preocupe com os nomes de classes e conceitos explicados acima como solução para a forma correta. Durante o desenvolvimento dos demais artigos, irei explicar todas as classes e os princípios aplicados.

O fonte utilizado aqui pode ser baixado pelo GITHub em https://github.com/desenvolvedores-net/ArtigoSOLID

 


É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

S.O.L.I.D

2
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (1 votos, média: 5,00 de 5)
Loading...
31 de julho de 2017

Use este post como um índice para os demais artigos, basta clicar nos links para ler os artigos.

Olá galera…

Estou aqui mais uma vez com uma série de artigos, desta vez irei falar sobre S.O.L.I.D. Sim, eu sei que existem centenas de artigos sobre o assunto por ai, mas minha intenção aqui é aprender um pouco mais sobre o assunto com alguns exemplos em C#.

Afinal, o que é S.O.L.I.D.?

O padrão S.O.L.I.D. são cinco princípios que nos ajudam a desenvolver aplicações mais robustas e de fácil manutenção. Vamos entender o que é cada um dos princípios nos tópicos abaixo.

Estes princípios foram definidos pelo Robert. C. Martin, ou se preferirem, Uncle Bob, e datam do início do ano 2000.

S.O.L.I.D. é um acróstico para os seguintes acrônimos :

Para ler em detalhes cada uma das siglas, basta clicar na sigla ou em sua definição.

SRP

Single Responsibility Principle (Princípio da Responsabilidade Única)

note-taking Uma classe deve ter um único, e somente um, motivo para que possa ser modificada.
A class should have one, and only one, reason to change.

OCP

Open/ Closed Principle (Princípio aberto/fechado)

note-taking Você deve ser capaz de estender um comportamento de uma classe sem modificá-lo.
You should be able to extend a classes behavior, without modifying it.

LSP

Liskov Subtitution Principle (Princípio da Substituição de Liskov)

note-taking As classes derivadas devem poder substituir suas classes bases.
Derived classes must be substitutable for their base classes.

ISP

Interface Segregation Principle (Princípio de segregação de interface)

note-taking Interfaces específicas são melhores do que uma interface geral
Make fine grained interfaces that are client specific.

DIP

Dependency Inversion Principle (Princípio da inversão de dependência)

note-taking Dependa de uma abstração e não de uma implementação.
Depend on abstractions, not on concretions.

Atenção

Durante o desenvolvimento dos exemplos, não vou usar nenhum ORM, Framework ou qualquer outra ferramenta de apoio para não confundir a nossa cabeça, irei apenas aplicar os conceitos de S.O.L.I.D nos exemplos.

Os fontes utilizados como exemplo podem ser baixados pelo GITHub em https://github.com/desenvolvedores-net/ArtigoSOLID

SaberMais Para saber mais.

 


É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

Polimorfismo (POO)

10
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (8 votos, média: 3,50 de 5)
Loading...
12 de fevereiro de 2011
Para um melhor entendimento deste Artigo veja o Índice (Programação Orientada a Objetos)
Iremos entrar agora em nosso último tópico sobre POO, e  muito interessante que é polimorfismo.Se você seguiu toda a trajetória do artigo sobre POO deve ter visto várias formas de polimorfismo (isso não foi um trocadilho) durante a leitura.

Vimos em:

Herança (POO)
Tipos Genéricos (Generics) (POO)
Abstração e Interface (POO)

Leia este artigo e depois do entendimento, volte a eles e tente identificar o uso do polimorfismo.

Aqui iremos abordar o polimorfismo de uma forma mais ampla e detalhada. Então vamos lá.

Polimorfismo

Etimologicamente, polimorfismo quer dizer:

1 Que se apresenta ou ocorre sob formas diversas.
2 Que assume ou passa por várias formas, fases etc.

Em orientação a objetos podemos dizer também que o objeto pode ter “vários comportamentos”.

Quando você chama um método ele pode se referir a qualquer objeto, por exemplo o método “Abrir()”, ele pode abrir uma tampa de garrafa, uma janela, uma porta, pode abrir alguma coisa.

Em polimorfismo é necessário que os métodos tenham a mesma assinatura, o mecanismo utilizado é o de redefinição de métodos (Overriding Definições – Intermediário (POO)) , não confundir com sobrecarga de métodos (Overload Definições – Intermediário (POO)).

Em polimorfismo podemos:

1 Instanciar objetos diferentes para um mesmo tipo.
2 Substituir condicionais com o polimorfismo.
3 Usar parâmetros que aceitam diferentes tipos de objetos desde que tenham a mesma herança de classes.

Vamos à alguns exemplos para reforçarmos as idéias.

Vamos criar nossas classes que servirão de base para todo o exemplo.

Iremos criar uma calculadora bem simples, que implemente as quatro operações básicas.

Classe Base

namespace Polimorfismo
{
    /// <summary>
    /// Classe base para efetuar os cálculos matemáticos
    /// </summary>
    abstract class CalculoMatematico
    {
        public virtual double Valor1 { get; set; }
        public virtual double Valor2 { get; set; }

        public abstract double Resultado();
    }
}

Multiplicação

namespace Polimorfismo
{
    class Multiplicacao : CalculoMatematico
    {
        public override double Resultado()
        {
            return Valor1 * Valor2;
        }
    }
}

Divisão

namespace Polimorfismo
{
    class Divisao : CalculoMatematico
    {
        public override double Resultado()
        {
            return Valor1 / Valor2;
        }
    }
}

Adição

namespace Polimorfismo
{
    class Adicao : CalculoMatematico
    {
        public override double Resultado()
        {
            return Valor1 + Valor2;
        }
    }
}

Subtração

namespace Polimorfismo
{
    class Subtracao : CalculoMatematico
    {
        public override double Resultado()
        {
            return Valor1 - Valor2;
        }
    }
}

Vamos agora ao primeiro exemplo.
Partindo de um mesmo tipo de objeto, podemos instanciar os outros tipos.
Mas para isso todos as classes devem implementar a mesma classe Pai.

namespace Polimorfismo
{
    class Program
    {
        static void Main(string[] args)
        {
            //veja que definimos o tipo
            //como CalculoMatematico
            CalculoMatematico calc;

            //aqui é criado como Adicao
            calc = new Adicao();
            calc.Valor1 = 23;
            calc.Valor2 = 34;
            Console.WriteLine(calc.Resultado().ToString("N2"));

            //aqui é criado como Divisao
            calc = new Divisao();
            calc.Valor1 = 23;
            calc.Valor2 = 34;
            Console.WriteLine(calc.Resultado().ToString("N2"));

            //aqui é criado como Subtracao
            calc = new Subtracao();
            calc.Valor1 = 23;
            calc.Valor2 = 34;
            Console.WriteLine(calc.Resultado().ToString("N2"));

            //aqui é criado como Multiplicacao
            calc = new Multiplicacao();
            calc.Valor1 = 23;
            calc.Valor2 = 34;
            Console.WriteLine(calc.Resultado().ToString("N2"));

            Console.ReadKey();
        }
    }
}

O nosso segundo item diz que podemos substituir condicionais com o uso de polimorfismo.
Será?
Vamos ver os exemplos abaixo, reparem bem na diferença entre os dois métodos da classe Calculadora

Definição da classe Calculadora:

namespace Polimorfismo
{
    public enum TipoCalculo
    {
        Adicao,
        Subtracao,
        Multiplicacao,
        Divisao
    }

    static class Calculadora
    {
        /// <summary>
        /// Neste método temos que informar o tipo
        /// de calculo que o mesmo irá fazer
        /// </summary>
        /// <param name="tipo">tipo do calculo feito</param>
        /// <param name="valor1">valor 1 para o calculo </param>
        /// <param name="valor2">valor 2 para o calculo</param>
        /// <returns>resultado da expressão</returns>
        public static double Calcular(TipoCalculo tipo, double valor1, double valor2)
        {
            double resultado = 0;

            //e aqui temos o condicional switch
            switch (tipo)
            {
                case TipoCalculo.Adicao:
                    resultado = valor1 + valor2;
                    break;
                case TipoCalculo.Subtracao:
                    resultado = valor1 - valor2;
                    break;
                case TipoCalculo.Multiplicacao:
                    resultado = valor1 * valor2;
                    break;
                case TipoCalculo.Divisao:
                    resultado = valor1 / valor2;
                    break;
                default:
                    throw new NotImplementedException("nunca poderá chegar aqui");
            }

            return resultado;
        }

        /// <summary>
        /// fazendo deste modo não precisamos
        /// utilizar nenhum condicional
        /// </summary>
        /// <param name="calc">tipo de calculo</param>
        /// <returns>resultado do calculo</returns>
        public static double Calcular(CalculoMatematico calc)
        {
            return calc.Resultado();
        }
    }
}

Exemplificando…

namespace Polimorfismo
{
    class Program
    {
        static void Main(string[] args)
        {
            //aqui vamos usar o condicional
            Console.WriteLine("Usando o Condicional");
            Console.WriteLine(Calculadora.Calcular(TipoCalculo.Adicao, 23, 34).ToString("N2"));
            Console.WriteLine(Calculadora.Calcular(TipoCalculo.Divisao, 23, 34).ToString("N2"));
            Console.WriteLine(Calculadora.Calcular(TipoCalculo.Multiplicacao, 23, 34).ToString("N2"));
            Console.WriteLine(Calculadora.Calcular(TipoCalculo.Subtracao, 23, 34).ToString("N2"));

            Console.WriteLine("".PadLeft(80, '-'));
            Console.WriteLine("Usando polimorfismo");
            //Aqui nao iremos usar o condicional.
            //substituiremos pelo uso do polimorfismo
            CalculoMatematico calc;

            //aqui é criado como Adicao
            calc = new Adicao();
            calc.Valor1 = 23;
            calc.Valor2 = 34;
            Console.WriteLine(Calculadora.Calcular(calc).ToString("N2"));

            //aqui é criado como Divisao
            calc = new Divisao();
            calc.Valor1 = 23;
            calc.Valor2 = 34;
            Console.WriteLine(Calculadora.Calcular(calc).ToString("N2"));

            //aqui é criado como Multiplicacao
            calc = new Multiplicacao();
            calc.Valor1 = 23;
            calc.Valor2 = 34;
            Console.WriteLine(Calculadora.Calcular(calc).ToString("N2"));

            //aqui é criado como Subtracao
            calc = new Subtracao();
            calc.Valor1 = 23;
            calc.Valor2 = 34;
            Console.WriteLine(Calculadora.Calcular(calc).ToString("N2"));

            Console.ReadKey();
        }
    }
}

Na classe Calculadora, além de exemplificarmos a substituição de um condicional com o uso do polimorfismo, ainda vimos a sobrecarga de métodos, temos dois métodos Calcular com assinaturas diferentes.

Para um melhor entendimento de sobrecarga recomendo a leitura do tópico Definições – Intermediário (POO).

E vimos também o item 3 que diz que podemos aceitar parâmetros de várias formas de objetos reparem que o método public static double Calcular(CalculoMatematico calc) aceitou os quatro tipos básicos.

Conclusão

Vimos o uso do polimorfismo em métodos, mas podemos usar a idéia em construtores, propriedades, no uso de Generics (Tipos Genéricos (Generics)), que podemos chamar de Polimorfismo Genérico.

Podemos citar rapidamente o uso de downcast (converter em uma classe mais específica) ,  upcast (definir em uma classe mais genérica).

namespace Polimorfismo
{
    class Program
    {
        static void Main(string[] args)
        {
            //definir objeto
            CalculoMatematico calc = new Adicao();
            calc.Valor1 = 22;
            calc.Valor2 = 33;

            //downcast (classe mais específica)
            Adicao adicao = (Adicao)calc;
            Console.WriteLine(adicao.Resultado());

            //upcast (classe mais genérica)
            calc = (CalculoMatematico)adicao;
            Console.WriteLine(calc.Resultado());

            Console.ReadKey();
        }
    }
}

Ver Índice

É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s

Herança (POO)

6
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (6 votos, média: 4,83 de 5)
Loading...
29 de janeiro de 2011
Para um melhor entendimento deste Artigo veja o Índice (Programação Orientada a Objetos)

Herança

Iremos entrar agora em um tópico que teremos muito o que discutir. Tentarei ser o mais simples , claro e objetivo.
Mas antes de falarmos sobre herança, iremos falar sobre a hierarquia das classes.

Hierarquia

Quando vamos trabalhar com muitas classes, temos que ter em mente a hierarquia (ordem) que estas classes deverão seguir.

Por exemplo:

Não poderemos ter os filhos antes dos pais e antes dos avós. A hierarquia correta para este tipo de classe seria:

Avô -> Pai -> Filho



(quando ví esta imagem, não resisti. Serve perfeitamente para o exemplo de hierarquia)

Assim temos a hierarquia bem definida dentro de nossas classes.

Transformando isso em programação teríamos generalização e especialização.

Como foi visto, a classe mais ao topo da hierarquia é a generalização, enquanto a última classe é a mais especializada.
No diagrama de seqüencia abaixo, podemos ver que as classes mais a esquerda são as genéricas e as mais a direita são as especializadas.

Agora vamos tratar dos tipos de herança que podemos ter.

Herança de Implementação

A herança de implementação é aquela onde herdamos todo o código da classe Pai, não precisamos reescrever código para utilizá-lo, estamos falando de reutilização de código.

Caso desejamos subsitituir um método da classe pai devemos usar overriding, como foi discutido em Definição (Intermediário).

Creio que a herança de implementação é a mais usada, pois herda a assinatura e os métodos da classe pai.

Herança de Interface

Caso não conheçam Interface e Abstração leiam este tópico.

A herança de interface é útil quando precisamos:

  • agrupar as nossas classes dentro de um mesmo contexto;
  • Na programação genérica (veja: Tipos Genéricos (Generics) );
  • Outros que vocês irão descobrir com o tempo;

Na herança de interface apenas herdamos as assinaturas de métodos e propriedades ao contrário da herança de implementação que além de herdar  as assinaturas herdamos também seu código, logo, no caso da interface temos que escrever o código do método ou propriedade.

Agregação

A agregação define uma dependência fraca entre as classes, isto quer dizer que os outros objetos continuam existindo mesmo que o todo for removido.
Podemos dizer que uma classe agregada (o todo) é montada com um conjunto de componentes (as partes).

Como exemplo vamos usar um carro.
Em nosso carro (classe agregada, o todo) temos os componentes (as partes), rodas, portas, motor.
Se desmontarmos o carro, as partes (rodas, portas, motor) continuaram a existir e poderão ser usadas em outro carro.

Composição

A composição define uma dependência forte entre as classes, isto quer dizer que se o todo deixar de existir as suas partes também deixaram de existir.

Exemplo:
Uma revista em quadrinhos, ela é composta por suas páginas. Se o todo, a revista, deixar de existir, suas páginas não farão sentido.

Como outro exemplo podemos citar um pedido e seus itens. Esta ligação é forte, logo é uma composição. Os itens não fariam sentido sem o Pedido. Se excluirmos o pedido seus itens serão excluídos. (Isto não te lembra um relacionamento feito em definição de tabelas? 1 para n ON DELETE CASCADE).

Para finalizarmos vamos colocar um diagrama de classes e um código para reforçar a idéia.

Diagrama acima convertido em código:

Interface IPessoa

namespace Heranca
{
    /// <summary>
    /// esta é uma interface.
    /// Percebam que declaramos apenas os métodos,
    /// iremos usar neste caso a herança de interface
    /// </summary>
    interface IPessoa
    {
        void Dancar();
        DateTime DataNascimento { get; set; }
        void Envelhecer();
        void Falar();
        int Idade { get; }
        string Nome { get; set; }
        Sexo Sexo { get;  }
    }
}

Classe Pessoa

namespace Heranca
{
    public enum Sexo
    {
        NaoDefinido,
        Masculino,
        Feminino
    }

    /// <summary>
    /// esta classe é uma classe genérica pra todo tipo de pessoa.
    /// vejam que aqui iremos herdar a interface IPessoa,
    /// neste caso será uma herança de interface.
    /// Devemos escrever código para todo método e propriedade
    /// definido na interface.
    /// </summary>
    abstract class Pessoa : IPessoa
    {
        /// <summary>
        /// esta propriedade poderá ser sobreescrita
        /// nas classes filhas. mas não é obrigatória
        /// </summary>
        public virtual int Idade
        {
            get
            {
                int idade = DateTime.Now.Year - DataNascimento.Year;
                if (DateTime.Now.Month < DataNascimento.Month ||
                (DateTime.Now.Month == DataNascimento.Month &&
                DateTime.Now.Day < DataNascimento.Day))
                    idade--;
                return idade;
            }
        }

        public DateTime DataNascimento { get; set; }

        /// <summary>
        /// esta propriedade deverá obrigatoriamente ser declarada
        /// na classe pai
        /// </summary>
        public abstract Sexo Sexo { get; }

        public virtual String Nome { get; set; }

        public virtual void Envelhecer()
        {
            DataNascimento = DataNascimento.AddYears(1);
            Console.WriteLine("A pessoa envelheceu um ano.");
        }

        public virtual void Dancar()
        {
            Console.WriteLine("A pessoa está dançando");
        }

        public virtual void Falar()
        {
            Console.WriteLine(@"A pessoa disse: visitem
            http://desenvolvedores.net");
        }
    }
}

Classe Pai

namespace Heranca
{
    /// <summary>
    /// Aqui iremos herdar a classe pessoa.
    /// vejam que apenas escrevi a propriedade Sexo.
    /// Pois na classe pai (abstrata) estava definido
    /// que as filhas deveria escrever o código para sexo.
    /// Mas as outras propriedades e métodos serão herdados de pessoa
    /// </summary>
    class Pai : Pessoa
    {
        public override Sexo Sexo { get { return Sexo.Masculino; } }

        /// <summary>
        /// este método poderá ser sobreescrito nas classes filhas
        /// neste caso o pai está apenas brincando.
        /// Veremos ele sobreescrito na classe filha
        /// </summary>
        public virtual void Brincar()
        {
            Console.WriteLine("O pai está brincando");
        }
    }
}

Classe Filho

namespace Heranca
{
    /// <summary>
    /// aqui o filho herdou todas as características do pai
    /// </summary>
    class Filho : Pai
    {

        /// <summary>
        /// aqui iremos substituir o método Brincar
        /// </summary>
        public override void Brincar()
        {
            Console.WriteLine("O filho está brincando com seu pai.");
        }
    }
}

Classe Aluno

namespace Heranca
{
    /// <summary>
    /// mais um classe que herda de pessoa
    /// </summary>
    class Aluno : Pessoa
    {
        public override Sexo Sexo
        {
            get { return Sexo.Feminino; }
        }

        public int VerNota()
        {
            return new Random().Next(0, 10);
        }
    }
}

E finalmente o código escrito em uma aplicação do tipo console.
Atenção, prestem bastante atenção aos comentários, eles têm dicas importantes.

namespace Heranca
{
    class Program
    {
        static void Main(string[] args)
        {
            /*
             * aqui vai um dica útil.
             * Cuidado ao declarar o seu tipo de objeto
             * pois as propriedades e métodos que você
             * irá visualizar depende do tipo declarado
             *
             * Vejam aqui, vou declarar como um objeto Pessoa
             * mas vou instanciar como um Aluno
             */

            Pessoa pessoaAluno = new Aluno();

            pessoaAluno.Nome = "Marcelo";

            /*
             * reparem que o método VerNota() só existe
             * na classe Aluno, logo na linha 17,
             * mesmo o objeto sendo instanciado como
             * new Aluno() seus métodos só existirão
             * através do seu tipo definido Pessoa
             */

            Console.WriteLine(pessoaAluno.Nome);

            /*
             * Mas, e se precisarmos acessar
             * os dados de Aluno?
             * Simplesmente faça um cast (conversão)
             * de um objeto para outro
             * Vejam:
             */

            Aluno aluno = pessoaAluno as Aluno;

            Console.WriteLine(aluno.VerNota());

            /*
             * A dica acima é válida tambem para os tipos
             * interfaces
             */

            IPessoa pessoaFilho = new Filho();
            pessoaFilho.Falar();

            Filho filho = pessoaFilho as Filho;
            filho.Brincar();

            /*
             * Abaixo um exemplo do override em filho
             */

            Pai pai = new Pai();

            //aqui chamamos o método brincar de Pai
            pai.Brincar();

            //aqui chamamos o método brincar que foi substituido
            Filho meuFilho = new Filho();
            meuFilho.Brincar();

            /*
             * Legal. Mas posso chamar o método
             * Brincar da classe base?
             */

            pai = filho as Pai;
            pai.Brincar();

            /*
             * Não. Não podemos.
             * Apesar da instrução acima não dar erro
             * o método que será chamado é sempre
             * o método substituído.
             *
             * Mas a afirmação acima não é 100% correta.
             * Se em nossa classe Filho o método tivesse
             * sido declarado assim:
             * public new void Brincar()
             * O método chamado seria o da classe Pai,
             * pois neste caso não substituimos o método
             * e sim criamos um novo.
             *
             * Vejam as duas declarações juntas para comparar
             *
             * public new void Brincar()
             * public override void Brincar()
             */

            Console.ReadKey();

        }
    }
}

Ver Índice

É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s

Encapsulamento (POO)

1
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (Sem votação.)
Loading...
17 de janeiro de 2011
Para um melhor entendimento deste Artigo veja o Índice (Programação Orientada a Objetos)

Entenda como encapsulamento o ato de proteger características de nossos objetos, isto quer dizer que o que não for público estará protegido de ação externa.

Como exemplo iremos pegar um motor. Não precisamos saber o funcionamento dele, apenas temos que saber que o método Ligar() da nossa classe Motor irá fazer com que o nosso motor entre em funcionamento.

Mas não precisamos saber quais componentes o método irá acessar, modificar ou criar para que o nosso motor entre em funcionamento.

O que o método ligar faz, não nos diz respeito, apenas usamos e sabemos que vai funcionar.

No conceito de orientação a objetos podemos proteger aquilo que não queremos que sofram intervenção, nossas variáveis locais, nossos métodos e atributos.

Os dados protegidos só podem ser alterados dentro dos métodos em que foram declarados ou dentro do objeto a que eles pertencem.

Exemplo:

namespace Desenvolvedores.Net.Encapsulamento
{
 public class Motor
 {
 /*
 * Aqui podemos apenas recuperar o valor de Ligado.
 * Mas percebam que para modificar o motor para Ligado
 * não iremos conseguir fazer isso pois a propriedade
 * está como Private, logo podemos dizer que está
 * encapsulada.
 * Só iremos conseguir modificar este valor
 * internamente, dentro desta classe.
 */
 public bool Ligado { get; private set; }

 public void Ligar()
 {
 //variável declarada localmente.
 //podemos dizer que está encapsulada dentro
 //do método Ligar e é acessível
 //somente dentro deste método
 bool temGasolina = TemGasolina();

 if (temGasolina)
 /*
 * aqui modificamos a propriedade Ligado para verdadeiro.
 * Veja que só podemos alterar por aqui ou outro
 * método dentro deste objeto e nunca externamente.
 */
 Ligado = true;
 }

 /*
 * Este método está encapsulado, pois está acessível apenas
 * dentro desta classe (objeto)
 */
 private bool TemGasolina()
 {
 return true;
 }

public override string ToString()
 {
 if (Ligado)
 return "O motor está ligado.";
 else
 return "O motor está desligado.";
 }
 }

 class Program
 {
 static void Main(string[] args)
 {
 Motor motor = new Motor();

 Console.WriteLine(motor.ToString());

 motor.Ligar();

 Console.WriteLine(motor.ToString());

 //se tentarmos forçar o motor a ser ligado iremos
 //receber um erro do compilador:
 //Erro em C#
 //The property or indexer 'Encapsulamento.Motor.Ligado'
 //cannot be used in this context because the
 //set accessor is inaccessible

 motor.Ligado = true;

 Console.ReadKey();
 }
 }
}

Ver Índice

É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s

Abstração e Interface (POO)

3
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (3 votos, média: 5,00 de 5)
Loading...
11 de janeiro de 2011
Para um melhor entendimento deste Artigo veja o Índice (Programação Orientada a Objetos)

Esta imagem não tem muito a ver com informática.
mas gostei dela por se tratar de uma imagem abstrata.
Abstração e Interface

Abstração
Para entendermos o que é abstração e interface em programação, vamos entender primeiro o conceito.Abstração quer dizer que devemos considerar as qualidades e comportamentos independentes dos objetos a que pertencem, isolamos seus atributos considerando o que certo grupo de objetos têm em comum.

Vamos imaginar uma mesa, todos nós iremos imaginar uma mesa que tenha pés, uma base e uma finalidade. Mas não importa quantos pés, ou tipo de base ou a finalidade da mesa. Isso não fará com que a mesa que eu imaginei, deixe de ser uma mesa. Ela sempre será uma mesa.

Em programação iremos dizer ao computador para ser um objeto mesa. Mas antes disso precisamos dizer a este objeto qual seria a sua estrutura inicial. Neste caso teremos uma classe abstrata Mesa.

Classes abstratas como vocês já devem ter percebido servem como base para outras classes que queiram ser do mesmo grupo de objetos.

Uma classe abstrata não pode ser criada, quer dizer que não pode ser instanciada. Não podemos usar o new para criar um novo objeto de uma classe abstrata. Uma classe abstrata só pode ser herdada, e todo método, propriedade, evento que for abstrato deverá ser implementado pela classe filha.
Vamos declarar a nossa classe Mesa, que irá servir de base para as outras mesas.

namespace Desenvolvedores.Net.AbstracaoInterface
{
public enum TipoMesa
{
Redonda,
Quadrada,
Retangular
}

public enum TipoBase
{
Vidro,
Madeira,
Aluminio
}

/*
* aqui declaramos a nossa classe
* como abstrata
*/
public abstract class Mesa : IMesa
{

public Mesa()
{
Pes = 4;
}

/*
* reparem que as propriedades não são abstratas,
* não é obrigatório.
* Declare como asbtrato apenas o que você deseja
* que seja implementado nas classes filhas
*/
public TipoBase Base { get; set; }
public TipoMesa Tipo { get; set; }
public int Pes { get; set; }

/*
* aqui irei ter propriedades abstratas para servir de exemplo
*/
public abstract bool Quebrada { get; set; }
public abstract string Finalidade { get; }

/*
* Aqui teremos um método abstrato, onde a classe filha
* será obrigatória a implementar
*/
public abstract void Quebrar();

public virtual void Consertar()
{
Quebrada = false;
}

public override string ToString()
{
return "Olá. Eu sou uma mesa do tipo " +
Tipo.ToString() + " e minha base é de " +
Base.ToString() + ".\nEu tenho " + Pes.ToString() + " pés. " +
"E minha finalidade é " + Finalidade.Trim() + "\n" +
"*".PadLeft(80, '*');
}

}
}

Agora temos uma classe abstrata que será nossa base para todas as nossas mesas.

Antes de continuar, vamos falar sobre interfaces.

Interfaces
Interfaces são como as classes abstratas, mas nelas não podemos implementar nenhum método, apenas declarar suas assinaturas.
Uma classe ao herdar uma interface deverá escrever todos os seus métodos (iremos ver isso em Herança de Interface nos próximos artigos), ao contrário da classe abstrata que só iremos escrever os métodos abstratos.
Com o tempo você aprenderá a ver qual é mais útil dependendo da situação é mais vantajoso usar uma classe abstrata ou uma interface. Não existe uma regra para definir isso, mas sim a necessidade de uma ou de outra.

Por boas práticas, todas as interfaces começa com I, vamos declarar nossa interface IMesa.

namespace Desenvolvedores.Net.AbstracaoInterface
{
interface IMesa
{
TipoBase Base { get; set; }
string Finalidade { get; }
bool Quebrada { get; set; }
TipoMesa Tipo { get; set; }
int Pes { get; set; }

void Consertar();
void Quebrar();
}
}

Percebam que a nossa interface IMesa tem todos os métodos e propriedades para fazer de outra classe uma Mesa, e vejam também que eu não escrevi nenhum código nela, apenas declarei as assinaturas dos métodos e propriedades. 🙂

Exemplo de Uso
Agora como exemplo de uso iremos criar alguns tipos de mesas.
Crie uma aplicação console em C#.

Copie os códigos abaixo.

Uma mesa redonda. Estamos herdando aqui a classe abstrata Mesa.

namespace Desenvolvedores.Net.AbstracaoInterface
{
/*
* repare que ao herdarmos a classe
* abstrata Mesa. Só escrevemos os métodos e propriedades
* que foram definidos como abstrato na classe pai Mesa.
*/
public class MesaRedonda : Mesa
{
public override bool Quebrada { get; set; }
public override string Finalidade
{
get { return "para discutir futebol."; }
}

public override void Quebrar()
{
Quebrada = true;
}
}
}

Agora aqui uma mesa de jantar, que também herda a classe abstrata Mesa.

namespace Desenvolvedores.Net.AbstracaoInterface
{
/*
* repare que ao herdarmos a classe
* abstrata Mesa. Só escrevemos os métodos e propriedades
* que foram definidos como abstrato na classe pai Mesa.
*/
class MesaDeJantar : Mesa
{
public override bool Quebrada { get; set; }

public override string Finalidade
{
get { return "para jantares em família."; }
}

public override void Quebrar()
{
Quebrada = true;
}
}
}

Agora aqui uma mesa de escritório. Nela herdamos a interface, reparem que tive que desenvolver todo o código para a classe filha, a interface apenas trouxe a assinatura dos métodos.

namespace Desenvolvedores.Net.AbstracaoInterface
{
/*
* Nesta classe herdamos apenas a interface IMesa.
* Vejam que tive que escrever todo o código para a classe.
* A interface trouxe apenas as assinaturas dos métodos.
*(herança de interface)
* No caso de uma classe abstrata não há a necessidade
* de se fazer todos os métodos (herança de implementação)
*/
class MesaEscritorio : IMesa
{
public MesaEscritorio()
{
Pes = 4;
}

public TipoBase Base { get; set; }

public string Finalidade
{
get { return "para trabalhar."; }
}

public bool Quebrada { get; set; }

public TipoMesa Tipo { get; set; }

public int Pes { get; set; }

public void Consertar()
{
Quebrada = false;
}

public void Quebrar()
{
Quebrada = true;
}

public override string ToString()
{
return "Olá. Eu sou uma mesa do tipo " +
Tipo.ToString() + " e minha base é de " +
Base.ToString() + ".\nEu tenho " + Pes.ToString() + " pés. " +
"E minha finalidade é " + Finalidade.Trim() + "\n" +
"*".PadLeft(80, '*');
}
}
}

E finalmente. A aplicação de exemplo:

namespace Desenvolvedores.Net.AbstracaoInterface
{
class Program
{
static void Main(string[] args)
{
//aqui criamos os nossos objetos da forma tradicional
MesaRedonda mesaRedonda = new MesaRedonda();
mesaRedonda.Base = TipoBase.Madeira;
Console.WriteLine(mesaRedonda.ToString());

MesaDeJantar mesaJantar = new MesaDeJantar();
mesaJantar.Tipo = TipoMesa.Retangular;
Console.WriteLine(mesaJantar.ToString());

MesaEscritorio mesaEscritorio = new MesaEscritorio();
mesaEscritorio.Tipo = TipoMesa.Quadrada;
Console.WriteLine(mesaEscritorio.ToString());
Console.WriteLine("Usando a interface e a classe abstrata " +
"para criar novas mesas\n" + "*".PadLeft(80,'*'));

//aqui iremos usar um pouco de polimorfismo
//que iremos discutir tambem em nossos próximos artigos
//logo, não se preocupe em saber a definição
//apenas atente para o uso da interface e da classe abstrata
//como tipo de variável

//a minha mesa pode ser de escitório.
IMesa iMesa = new MesaEscritorio();
iMesa.Tipo = TipoMesa.Redonda;
iMesa.Base = TipoBase.Madeira;

Console.WriteLine(iMesa.ToString());

//de jantar
iMesa = new MesaDeJantar();
iMesa.Base = TipoBase.Aluminio;
Console.WriteLine(iMesa.ToString());

//ou redonda
iMesa = new MesaRedonda();
Console.WriteLine(iMesa.ToString());

//e alem da interface eu posso usar a própria classe
//abstrata para definir o meu tipo de mesa
Mesa mesa = new MesaDeJantar();
Console.WriteLine(mesa.ToString());

mesa = new MesaRedonda();
mesa.Tipo = TipoMesa.Quadrada;
mesa.Base = TipoBase.Vidro;
Console.WriteLine(mesa.ToString());

Console.ReadKey();
}
}
}

Bom. espero ter respondido às dúvidas sobre abstração.
Só para reforçar, é simples. Deixe nas classes abstratas ou interfaces, todos os métodos que serão comuns aos seus objetos do mesmo grupo. Assim sendo, sempre que você declarar uma variável de um tipo Interface ou classe Abstrata poderá criar qualquer tipo de objeto que implemente uma das duas.

Ver Índice

É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s

Definições – Avançado (POO)

4
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (3 votos, média: 4,33 de 5)
Loading...
7 de janeiro de 2011
Para um melhor entendimento deste Artigo veja o Índice (Programação Orientada a Objetos)
Generalização
Generalização é o ato de tornar um objeto geral, agrupar características comuns para objetos dentro de um mesmo contexto, abstrair.
Eu tenho, por hábito e não por regra, a minha classe “Pai de Todas”(superclass) ser sempre abstrata.Veja o diagrama:
Neste caso a minha classe “Pai de Todas”(superclass) é a classe “Pessoa” 

    

Se olharmos de cima para baixo podemos ver todos os objetos gerais, no topo temos a Pessoa, que pode ser um Cliente, um Usuário. O Usuário por sua vez pode ser um Administrador, que tem poderes especiais, o mesmo pode Excluir ou Inserir novos usuários.
E se olharmos de baixo para cima? Aí teremos o próximo tópico Especialização. 🙂

  

Especialização
A especialização nada mais é do que a parte que “especializa” o objeto vindo de uma Generalização, trazer características próprias para o objeto. Seria o mesmo que olhar o diagrama acima de baixo para cima.
Veja:
O objeto Administrador  especializa o objeto Usuario que por sua vez especializa o objeto Pessoa.    

  

Delagates
Os delegates  são ponteiros para funções (métodos).

O que são ponteiros?
Ponteiro é um valor atribuído em memória que aponta para outro valor em memória através de seu endereço. (não gostei, técnico demais pro meu gosto). 
 

 

Quando há a necessidade de usarmos métodos, e ainda não sabemos qual método chamar, pois dependemos de algumas condições, podemos deixar o nosso código mais bonito usando um delegate.   

O delegate irá chamar o método definido de acordo com a nossa necessidade ou condição.   

Veja exemplo:
Abaixo temos um exemplo de uma aplicação Console em C#. 

static class Program
    {
        //declara a assinatura do delegate.
        //os métodos deverão ter a mesma assinatura
        //para que possam ser utilizados pelo delegate
        delegate int Calular(int a, int b);

        static void Main()
        {

            //declara a variável do tipo delegate
            Calular calc;

            //uma condição qualquer
            if (DateTime.Now.Second % 2 == 0)
                //define o método dividir para ser usado no delegate
                calc = new Calular(Dividir);
            else
                //define o método somar para ser usado no delegate
                calc = new Calular(Somar);

            //chama o método
            Console.WriteLine(calc.Invoke(DateTime.Now.Millisecond,
DateTime.Now.Second));

            Console.ReadKey();
        }

        //declara o método somar
        private static int Somar(int a, int b)
        {
            return a + b;
        }

        private static int Dividir(int x, int y)
        {
            return x / y;
        }
    }

Eventos
Eventos são mensagens trocadas pelos sistemas, métodos e objetos para informar um acontecimento, uma ação que se inicia,  que termina dentro da aplicação, como o próprio nome diz, um evento ocorrido.
Vejamos, ao chamar o nosso método Andar() em Pessoa podemos informar que a Pessoa está Andando até que pare de andar , ou podemos informar que a pessoa vai parar de Andar antes de realmente ela parar de Andar. (AntesPararAndar), assim sendo o objeto chamante pode executar alguma ação antes de a Pessoa realmente parar de Andar.  

 Exemplo de eventos em C#:
Para usar eventos em C# temos que fazer uso de delegates como foi visto acima o delegate e um ponteiro para uma função (método), e eventos usam delegates para apontar a qual método o mesmo deverá chamar.   

Declaração de Pessoa

public class Pessoa
    {  
        //declaração da assinatura do delegate
        //que irá tratar os eventos
        public delegate void AndarHandler();

        //este evento irá mostrar que podemos esperar
        //retorno do objeto que chamou
        //o parâmetro pararAndar irá retornar true or false
        //dependendo do caso se for ou não para parar de andar
        public delegate void AndandoHandler(out bool pararAndar);

        //declaração dos eventos
        public event AndarHandler AntesAndar;
        public event AndarHandler AntesPararAndar;

        //repare aqui que iremos usar o segundo delegate
        public event AndandoHandler Andando;
       
        //declaração do método andar
        public void Andar()
        {
            //aqui iremos chamar o evento AntesAndar e avisar
            //o objeto chamante que iremos começar a Andar.
            AntesAndar();

            DateTime pararAs = DateTime.Now.AddSeconds(30);

            //aqui iremos andar por 30 segundos
            while (pararAs > DateTime.Now)
            {
                bool pararAgora = false;
                //aqui iremos notificar que a
                //pessoa continua Andando
                Andando(out pararAgora);

                if (pararAgora)
                {
                    //antes de parar, notificar que vou parar
                    AntesPararAndar();
                    break;
                }

                //aqui apenas iremos parar por um segundo.
                //não se preocupem com esta linha
                System.Threading.Thread.Sleep(1000);
            }
        }
    }

Declaração do objeto para testes.
É uma aplicação do tipo Console. 

static void Main()
        {
            //cria uma pessoa
            Pessoa marcelo = new Pessoa();

            //define os métodos que irão tratar os nossos eventos
            marcelo.Andando +=
new Pessoa.AndandoHandler(marcelo_Andando);
            marcelo.AntesAndar +=
new Pessoa.AndarHandler(marcelo_AntesAndar);
            marcelo.AntesPararAndar +=
new Pessoa.AndarHandler(marcelo_AntesPararAndar);

            //chamar o método para que marcelo possa Andar
            marcelo.Andar();

            Console.WriteLine("Marcelo parou de andar.");

            Console.ReadKey();

        }

        static void marcelo_AntesPararAndar()
        {
            Console.WriteLine("Marcelo irá parar de andar.");
        }

        static void marcelo_AntesAndar()
        {
            Console.WriteLine("Marcelo irá começar a andar.");
        }

        static void marcelo_Andando(out bool pararAndar)
        {
            pararAndar = false;
            //a pessoa marcelo só vai parar se a condição for verdadeira
            if (DateTime.Now.Second % 7 == 0)
                pararAndar = true;

            Console.WriteLine("Marcelo está andando.");
        }
SaberMais Para saber mais sobre delegates.

Foi escrito um novo artigo com tópicos avançados sobre delegates.

Delegates

Com isso terminamos a definição de POO. Aguardem agora os próximos artigos que vamos abordar nesse assunto, ainda temos muita coisa para ver.

Ver Índice

É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s

Definições – Intermediário (POO)

4
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (7 votos, média: 4,71 de 5)
Loading...
19 de dezembro de 2010
Para um melhor entendimento deste Artigo veja o Índice (Programação Orientada a Objetos)
Mensagens:
As mensagens são bem simples de entender, mensagens são informações trocadas entre um objeto e outro.
Estas mensagens podem modificar o comportamento do objeto a quem a mensagem foi direcionada,
ou retornar um valor a quem pediu.
A interação dos objetos é feito através de mensagens.Ex: É uma chamada para invocar um de seus métodos.

Pessoa pessoa = new Pessoa();
pessoa.Comer("Maçã");

Neste caso a chamada do método Comer(string alimento) em pessoa gerou uma mensagem,
e indicou ao objeto pessoa para comer uma maçã.

Vejamos outro exemplo:

Pessoa pessoa = new Pessoa();
//aqui Sonhar() retorna true
if (pessoa.Sonhar())
Console.WriteLine("A pessoa está sonhando");

Neste caso o objeto retornou uma informação a quem o chamou, o método retornou que a pessoa esta sonhando.

Overriding:
Override nada mais que é que sobrescrever, substituir o método da classe pai por um método escrito na classe filha.
Você só pode usar o override em métodos que permitam serem sobrescritos.
Ex:
A classe Marcelo (sou eu … rs) herda de pessoa seu comportamento, atributos e todo o mais.
Mas Marcelo não gosta de maçãs, logo a classe Marcelo vai sobrescrever o método Comer(string alimento).
Vejamos como fica:

class Marcelo : Pessoa
{

public override void Comer(string alimento)
{
if (alimento == "Maçã")
Console.WriteLine("Eu não gosto de maçãs. Não vou comer.");
else
base.Comer(alimento);
}
}

Overload:
Ao pé da letra sobrecarga de métodos, significa ter métodos com o mesmo nome e assinatura* diferente.
*Assinatura:
Assinatura de um método é como ele foi declarado, seu nome completo, nome e sobrenome (parâmetros).
Ex: Veja o método Comer(string alimento) podemos dizer que Comer é seu nome e string alimento
é seu sobrenome (parâmetros) que podem ser um ou mais.
Veja o método Comer na classe Pessoa.

public class Pessoa
{

public string Nome { get; set; }

public void Andar()
{ }

public virtual void Comer()
{ }

public virtual void Comer(string alimento)
{
Console.WriteLine("Comendo " + alimento);
}

public void Falar()
{ }

public Boolean Sonhar()
{
return true;
}
}

Persistência:
É a capacidade de um objeto de salvar seus dados para uso em outro momento.
Este termo é muito utilizado quando falamos de banco de dados, onde um objeto salva os dados em uma tabela.
Imaginamos que no objeto pessoa temos as propriedades, Nome, Telefone e CPF e precisamos salvar
estes dados para que quando o aplicativo for fechado possamos recuperar o mesmo ao abrir o aplicativo novamente.

Existem dois tipos de dados, transientes ou persistentes:

Para reforçar:
Dados Transientes: São dados que são válidos apenas dentro do programa ou transação.
Quando o programa é fechado ou a transação termina os dados se perdem
Dados Persistentes: São armazenados fora do contexto do programa,
existem mesmo quando o programa for fechado e
podem ser recuperados a qualquer momento.

Exemplo em CSharp de como persistir (serializar) um objeto

Declaração da classe Pessoa:

public class Pessoa
{
public string Nome { get; set; }

public string Telefone { get; set; }

public string CPF { get; set; }
}

Exemplo da serialização:

//popular o objeto pessoa
Pessoa pessoa = new Pessoa();
pessoa.Nome = "Marcelo";
pessoa.CPF = "000.000.000-00";
pessoa.Telefone = "(00) 0000-0000";

//criar o objeto que irá serializar a pessoa em XML
System.Xml.Serialization.XmlSerializer x =
new System.Xml.Serialization.XmlSerializer
(pessoa.GetType());

//salvar no arquivo XML pessoa.xml
System.Xml.XmlTextWriter xmlWriter =
new System.Xml.XmlTextWriter("pessoa.xml", Encoding.UTF8);

//serializar para pessoa.xml
x.Serialize(xmlWriter, pessoa);

//liberamos o arquivo xml
xmlWriter.Flush();
xmlWriter.Close();

//Neste momento se você abrir o arquivo pessoa.xml
//em algum editor
//verá os dados do objeto pessoa
System.Xml.XmlTextReader xmlReader =
new System.Xml.XmlTextReader("pessoa.xml");

//recuperar os dados e popular o objeto pessoa.
pessoa = (Pessoa)x.Deserialize(xmlReader);

//como teste, iremos passar o nome para uma variável string
string nomePessoa = pessoa.Nome;

Esta ilustração mostra o processo geral de serialização.

Ver Índice

É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s

Definições – Básico (POO)

3
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (10 votos, média: 5,00 de 5)
Loading...
17 de dezembro de 2010
Para um melhor entendimento deste Artigo veja o Índice (Programação Orientada a Objetos)
Nós, como seres humanos distinguimos estes objetos em três formas de pensamento: 

  • Diferenciação;
  • Distinção entre o todo e a parte;
  • Classificação;

Vemos nestas três formas, dentro da orientação a objetos, uma forma de diminuir a diferença semântica entre o que é real e o que é modelo.

O “homenzinho” acima é chamado de stickman.
É a representação dentro de um UseCase (UML)

O que é um objeto?
Podemos dizer que um objeto é algo quem tem características, ações, possui comportamento, uma identidade, distingui-se um do outro.
Os objetos podem ser físicos:
Um carro, uma casa, um pessoa, um cachorro …
Um conceito:
Um organograma, uma idéia …
Uma entidade:
Um botão, uma combobox, uma planilha Excel® …

O conjunto de ações, qualidades, as mudanças deste objeto influenciam no seu comportamento..
Podemos traduzir para a OO os termos, ações, qualidades, mudanças e comportamento da seguinte forma:

  • Ações:
    • O que o objeto faz, o verbo. Estes serão nossos Métodos.
      • Ex: Andar, Falar, Comer, Quebrar, Salvar.
  • Qualidades:
    • Serão nossos adjetivos, qualificam nossos objetos. Estes serão nossas Propriedades.
      • Ex: Cor, Tipo, Inquebrável, Esfomeado.
  • Mudanças:
    • Estas serão responsáveis por definir o Estado do nosso objeto.
    • Estes por sua vez podem se modificar ao longo do tempo.
      • Ex: Com fome, Andando, Quebrando, Comendo.
  • Comportamento:
    • O comportamento determina como este objeto responde às mudanças de suas qualidades e as ações que ele pode realizar.
      • Ex: João está com fome* porque é esfomeado**, logo ele tem que comer***.
        * com fome: Mudança de estado;
        ** esfomeado: Qualidade, adjetivo;
        *** comer: Ação, verbo
        Logo o comportamento do João pode modificar-se pelo fato de estar com fome.

Instância de um Objeto:
Imagine que temos o objeto Pessoa,  este objeto apenas representa uma pessoa, mas se quisermos dar uma identidade a este objeto temos que instanciá-lo, seria como dizer, trazer esta pessoa à vida, criar um novo objeto, um novo carro, um novo botão, uma nova planilha.

Classes:
Uma classe é a descrição (código) de um grupo de objetos com propriedades, comportamento, relacionamentos com outros objetos (associações e agregações).
Um objeto é uma instância de uma classe.

Modificadores deAcesso e Visibilidade:
Os modificadores, como o próprio nome diz, modificam as declarações de propriedades, métodos e classes, eles podem ser:

C# Java Descrição
Public Public Declaram queo método, propriedade ou classe é publica.
Isso quer dizer que está acessível a todos.

//declaração daclasse
public class Pessoa
{
//em uma propriedade
public string Nome { get; set; }

//em um método
public void Comer()
{
}
}
Private Private Declaram que o método, propriedade ou classe é privativo.
Isso quer dizer que está acessível apenas localmente.
Dentro do mesmo objeto.

//declaração daclasse
private class Pessoa
{
//em umapropriedade
private string Nome { get; set; }

//em um método
private void Comer()
{
}
}
Static Static Declaram quea classe não precisa ser instanciada.
Isso quer dizer que não é preciso criar um “novo” objeto,
podemos simplesmente chamar seus métodos e propriedades.

//declaração daclasse
public static class Pessoa
{
//em uma propriedade
public static string Nome { get; set; }

//em um método
public static void Comer()
{
}
}
Const Final Declara um variável constante.
Isso quer dizer que seuvalor não pode ser modificado.

public class Pessoa
{
public const string CPF = "000.000.000-00";
}
Sealed Final Uma classe “selada” não pode ser modificada nem herdada.

//declaração daclasse
public sealed class Pessoa
{
public string Nome { get; set; }

public void Comer()
{
}
}
Internal Package Apenas as classes que fazem parte do mesmo assembly
(executável, DLL) podem acessar esta classe ou método.
No caso do Java as que fazem parte do mesmo “Pacote”.

//na declaração daclasse
internal class Pessoa
{
//na declaração de uma propriedade
internal string Nome { get; set; }

//na declaração deum método
internal void Comer()
{
}
}
Protected Protected Apenas as classes filhas podem herdar estes métodos.
É visível apenas na classe pai e nas filhas.
Não são visíveis publicamente.

public class Pessoa
{
//na declaração de uma propriedade
protected string Nome { get; set; }

//na declaração deum método
protected void Comer()
{
}
}
Internal  Protected É a junção de Internal e Protected.
Isso quer dizer que apenas as classes do mesmo assembly
podemver seus métodos e herdar  suas características.

class Pessoa
{
//na declaração de uma propriedade
internal protected string Nome { get; set; }

//na declaração deum método
internal protected void Comer()
{
}
}
Abstract Abstract Declaram quea classe ou método será abstrato.
Isso quer dizer que o método deverá ser implementado na classe filha.
Classes abstratas não podem ser criados como
“novo objeto” só podem ser herdadas.

//na declaração daclasse
abstract class Pessoa
{
//na declaração de uma propriedade
public abstract string Nome { get; set; }

//na declaração de um método
public abstract void Comer();
}
Interface Interface Declara quea classe possui apenas a assinatura dos métodos e propriedades.
Todos os seus métodos e propriedades deverão ser implementados.

//na declaração da interface
public interface Pessoa
{
//interface apenas implementam a
//assinatura da propriedade
string Nome { get; set; }

//interface apenas implementam a
//assinatura dos métodos
void Comer();
}
Virtual Virtual Declara que o método/ propriedade pode ser sobrescrito (override)

//na declaração daclasse
public class Pessoa
{
//na declaração da propriedade
public virtual string Nome { get; set; }

//na declaração do método
public virtual void Comer() { }
}
Void Void Indica que o método não tem retorno.

public class Pessoa
{
//na declaração do método indica que
//não tem retorno
public virtual void Comer() { }

//Este método tem retorno não podemos
//usar o VOID e sim o tipo de retorno
public virtual Boolean Sonhar()
{
//retorna true se foi possível sonhar
return true;
}
}

Com isso temos uma visão geral dos modificadores mais comuns. Claro. Posso ter esquecido de alguns. 🙂
Estes modificadores podem ser combinados de acordo com a necessidade. Mas isso você irá aprender com o tempo, aqui o intuito é explicar o que são eles e para que servem.

Construtores e Destrutores:
O construtor é um método utilizado para inicializar os objetos da classe quando estes são criados.
Este método possui o mesmo nome da Classe e não tem nenhum tipo de retorno, nem mesmo void.
No construtor podemos iniciar todos os outros objetos e propriedades, ele será sempre chamada ao iniciar o objeto.

O destrutor é chamado quando o objeto é descarregado da memória.
Neste método podemos descarregar todos os outros objetos que usamos durante o tempo de vida da classe em memória

public class Pessoa
{
//o Nome da pessoa será iniciado quando construtor for chamado
public string Nome { get; set; }

//construtor sem parâmetros
public Pessoa()
{
//aqui o nome da pessoa é vazio
Nome = "";
}

//construtor com parâmetros
public Pessoa(string _nome)
{
//aqui o nome da pessoa é o mesmo do parâmetro _nome
Nome = _nome;
}

//aqui o destrutor
~Pessoa()
{
//podemos limpar os nossos objetos
Nome = null;
}
}

Atributo e Propriedade:
Muitas pessoas dizem que atributos e propriedades são às mesmas coisas. Eu costumo dizer que não.
Atributos são variáveis que guardam os valores das propriedades, e as propriedades são as qualidades de nossas classes visíveis ao mundo externo.
Veja:

public class Pessoa
{
//esta declaração é um atributo, pois armazena o valor da propriedade Nome
//normalmente são visíveis apenas local.
private string mNome = "";

//o Nome é uma propriedade.
//normalmente são públicas
public string Nome
{
// aqui usamos o atributo mNome para retornar o valor da propriedade
get { return mNome; }

// aqui usamos o atributo mNome para salvar o valor da propriedade
set { mNome = value; }
}
}

Métodos:
Os métodos serão os “verbos”, as ações que nossos objetos podem executar.

public class Pessoa
{
public void Andar()
{ }

public void Comer()
{ }

public void Falar()
{ }

public Boolean Sonhar()
{
return true;
}
}

Ver Índice

É isso aí pessoal 🙂
Até o próximo
♦ Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s

Um pouco de história (POO)

5
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (3 votos, média: 4,33 de 5)
Loading...
15 de dezembro de 2010
Para um melhor entendimento deste Artigo veja o Índice (Programação Orientada a Objetos)
 

1967: Simula – introduz os primeiros conceitos de OO
1972: Smalltalk – Xerox
1980: C++ – linguagem híbrida, derivada da linguagem C.
1983: Ada – criada para uso militar nos EUA
1984: Eilffel – primeiras características formais de OO
1986: Object pascal
1995: JAVA – Linguagem puramente orientada a objetos
1995: Várias linguagens agregando conceitos de OO
2000: Plataforma .NET

A Programação Orientada ao Objeto (Object-Oriented Programming) foi concebida há muito tempo atrás (no inicio da década de 70), a sua origem vem da linguagem Simula (Simula Language), concebida na Noruega na década de 60, e como o nome indica, foi criada para fazer simulações; entretanto, seu uso alavancou um conceito que até então passava desapercebido pela maioria dos projetistas: a similaridade com o mundo real.
A primeira linguagem de programação a implementar os conceitos de OOP foi a linguagem SIMULA-68; em seguida surgiu a linguagem Smalltalk; criada pela Xerox, que pode ser considerada a linguagem que popularizou e incentivou o emprego da OOP.
O resultado foi uma linguagem de pura linhagem OO, poderosíssima, que implementa todos os conceitos de OO, o que não acontece com as chamadas linguagens OO híbridas que implementam apenas alguns conceitos de orientação ao objeto.
Fundamentalmente o que se deseja com esta metodologia são basicamente duas características: reutilização de código e modularidade de escrita.
Formalmente, para ser considerada uma linguagem OO, esta precisa implementar quatro conceitos básicos: abstração, encapsulamento, herança e polimorfismo.

Um pouco de história das linguagens

Simula

A primeira linguagem a incorporar facilidades para definir classes de objetos genéricos na forma de uma hierarquia de classes e subclasses.
Foi idealizada em 1966, na Noruega, como uma extensão da linguagem ALGOL 60.
Uma classe em Simula é um módulo englobando a definição da estrutura e do comportamento comuns a todas as suas instâncias (objetos).

Smalltalk

Smalltalk foi desenvolvida no Centro de Pesquisas da Xerox durante a década de 70 e incorporou idéias de Simula.
Criou o princípio de objetos ativos, prontos a reagir a mensagens que ativam comportamentos específicos do objeto.

C++

Questões no projeto de C++

  • Ser melhor do que C
  • Suportar abstração de dados
  • Suportar programação orientada a objetos

C++ foi projetada para dar suporte a abstração de dados e programação orientada a objetos

C++ não impõe um paradigma

Ada

Ada é uma linguagem de programação criada através de um concurso realizado pelo U.S. Departament of Defense (DoD)
O principal projetista da equipe foi o francês Jean Ichbiah.
Esse concurso foi feito para por ordem na situação, o DoD em 1974 usava cerca de 450 linguagens ou dialetos de programação.
A linguagem foi primeiramente padronizada em 1983 pelo ANSI e em 1985 a Organização Internacional de Padronização (ISO).

Eiffel

Eiffel é uma Linguagem de Programação avançada, puramente orientada a objeto que enfatiza o projeto e construção de software reutilizável e de alta qualidade.
Eiffel foi criada por Bertrand Meyer que tinha uma extensa experiência com programação orientada a objeto, particularmente com SIMULA.

Object Pascal

O Object Pascal é uma linguagem orientada a objetos, isto é, todas as informações são tratadas como objetos e todos estes objetos pertencem a uma classe, que são categorias de objetos.
Delphi / Kylix / Lazarus são exemplos de ferramentas que utilizam esta linguagem.

Java

O Java é ao mesmo tempo um ambiente e uma linguagem de programação desenvolvida pela Sun Microsystems Inc.
Trata-se de mais um representante da geração de linguagens orientadas a objetos e foi projetado para resolver os problemas da área de programação cliente/servidor.
Os aplicativos em Java são compilados em um código de bytes (“bytecodes”) independente de arquitetura.
Esse código de bytes pode então ser executado em qualquer plataforma que suporte um interpretador Java.
O Java requer somente uma fonte e um binário e, mesmo assim, é capaz de funcionar em diversas plataformas, o que faz dele um sonho de todos os que realizam manutenção em programas.

Plataforma .NET

A plataforma .NET baseia-se em um dos princípios utilizados na tecnologia Java (Just In Time Compiler – JIT), os programas desenvolvidos para ela são duplo-compilados (compilados duas vezes), uma na distribuição (gerando um código que é conhecido como “bytecodes”) e outra na execução.
Um programa é escrito em qualquer das mais de vinte linguagens de programação disponíveis para a plataforma, o código fonte gerado pelo programador é então compilado pela linguagem escolhida gerando um código intermediário em uma linguagem chamada MSIL (Microsoft Intermediate Language).
Este novo código fonte gera um arquivo na linguagem de baixo nível Assembly, de acordo com o tipo de projeto.

Ver Índice

É isso ai pessoal 🙂
Até o próximo
 ♦ Marcelo

About Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil. Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software. Para saber mais ... http://desenvolvedores.net/marcelo []'s