ORIENTACAO A OBJETOS - CONCEITO

KERPLUNK 18/12/2013 15:10:31
#432270
Segundo o Macoratti:
Os princípios SOLID devem ser aplicados no desenvolvimento de software de forma que o software produzido tenha as seguintes características:

Seja fácil de manter, adaptar e se ajustar às constantes mudanças exigidas pelos clientes;
Seja fácil de entender e testar;
Seja construído de forma a estar preparado para ser facilmente alterado com o menor esforço possível;
Seja possível de ser reaproveitado;
Exista em produção o maior tempo possível;
Que atenda realmente as necessidades dos clientes para o qual foi criado;


Isso vai ser coberto logo logo, nem lembro mais qual era o próximo tópico pra explicar...
KERPLUNK 19/12/2013 17:54:32
#432344
Depois de termos entendido as List<T> que foi a última coisa que expliquei aqui, gostaria de entrar no conceito básico de Reflection e Decorações.
Reflection é uma maneira de uma classe [Ô]olhar para ela mesma[Ô]. é meio complicado exemplificar, mas vou tentar: Nos exemplos anteriores, falei de uma classe Cliente, com três propridades(Código, Nome e Data de nascimento). Imagine agora que eu queira, de dentro da própria classe, obter uma lista de todas as propriedades de cliente, para algum motivo qualquer, vamos dizer, para fazer um log. Vejamos como ficaria isso. Imagine o código abaixo em um form:

Cliente cli = new Cliente();
cli.Codigo = 1;
cli.Nome = [Ô]Jose da Silva[Ô];
cli.DataNascimento = new DateTime(1964, 4, 5);// = 5 de abril de 1964
cli.Gravar();

Bem simples, instancio uma classe [Ô]Cliente[Ô], atribuo valor às propriedades, e chamo um método [Ô]Gravar[Ô]. Agora, o que teríamos dentro desse método gravar? Ora, o código necessário para pegar os dados da instância e gravar no banco:

public void Gravar()
{
using (SqlConnection cn = new SqlConnection([Ô]minha connectionstring[Ô]))
{
using (SqlCommand cmd = new SqlCommand([Ô]insert into clientes (Codigo, Nome, DataNascimento) values (@Codigo, @Nome, @DataNascimento)[Ô], cn)
{
cmd.Parameters.AddWithValue([Ô]@Codigo[Ô], this.Codigo);
cmd.Parameters.AddWithValue([Ô]@Nome[Ô], this.Nome);
cmd.Parameters.AddWithValue([Ô]@DataNascimento[Ô], this.DataNascimento);
cmd.ExecuteNonQuery();
}
}
}


Um método simples, que usa uma conexão, um comando, atribui os valores aos parâmetros do comando e executa. Nesse caso, usando o objeto genérico [Ô]this[Ô], estou me referindo a própria instância da classe, que vai conter os valores de propriedades que eu atribuí, ou seja, Codigo = 14, Nome = Jose da Silva e DataNascimento = 5/4/1968. Após atribuir os valores à cada parâmetro da Query, o comando é executado, finaliza o bloco using do comando(dando o Dispose) e finaliza o bloco using da conexão(fechando-a e dando o Dispose). Certo, mas onde entra o Reflection nisso? Bem, digamos que antes de gravar no banco, eu queira criar um log desse registro que vou inserir, ou mandar esses dados para um e-mail, ou qualquer outra coisa. Não pense nisso agora, pense apenas no conceito do Reflection que vamos ver. Vamos criar um método dentro da classe cliente, chamado [Ô]Log[Ô]:

private void Log()
{
List<PropertyInfo> properties = this.GetType().GetProperties().ToList();
StringBuilder valores = new StringBuilder();
foreach (PropertyInfo item in properties)
{
valores.Append(string.Format([Ô]{0}={1}[Ô], item.Name, item.GetValue(this, null).ToString()));
}
File.AppendAllText([Ô]C:\\logs\\log.txt[Ô], valores.ToString());
}


Ok, parece meio confuso no inicio, eu sei. Mas vamos entender o que esse método faz:
[txt-color=#0000f0]List<PropertyInfo> properties = this.GetType().GetProperties().ToList();[/txt-color]
Essa linha, cria uma List<T> de PropertyInfo, uma classe do Framework(2.0 pra cima), que [Ô]descreve[Ô] uma propriedade qualquer. Nessa lista eu coloquei todas as propriedades de [Ô]this[Ô], que no caso, como já dito, se refere a instância atual da classe Cliente. A chave aqui é o método [Ô]GetType[Ô]. Ele é o responsável por [Ô]olhar para a própria classe[Ô] Cliente. Este método retorna um [Ô]Type[Ô], que é um tipo, ou uma entidade, no caso, Cliente. Type, é uma classe, que possui o método [Ô]GetProperties[Ô], que retorna um array contendo todas as propriedades do Type retornado por [Ô]GetType[Ô] e o último método é [Ô]ToList[Ô], que transforma o array retornado por [Ô]GetProperties[Ô] em um List. Ufa! Complicado hein? Um pouco, mas vamos entender quebrando esse comando todo em vários ao invés de em uma única linha:

List<PropertyInfo> properties = null;
Type tipoCliente = this.GetType();
PropertyInfo[] arrPropriedades = tipoCliente.GetProperties();
properties = arrPropriedades.ToList();
[c]
Essas quatro linhas fazem o mesmo que a linha azul, mas em etapas. Eu prefiro uma linha só, mas fica a critério de cada um.

Certo, e depois o que é feito com a List<T> de PropertyInfo?
Qualquer coisa que se queira fazer com a instância de cliente. No nosso caso, eu a percorro usando um bloco foreach e para cada propriedade de Cliente, adiciono em um StringBuilder, o seu nome e seu valor, no caso o arquivo [Ô]C:\log\log.txt[Ô] ficaria algo como:
[c]
Codigo=1
Nome=Jose da Silva
DataNascimento=1968/5/4 00:00:00

Esse método poderia ser adicionado ao processo de gravação(método Grava) e teríamos um log gravado cada vez que chamássemos o método Gravar, que ficaria assim:

public void Gravar()
{
using (SqlConnection cn = new SqlConnection([Ô]minha connectionstring[Ô]))
{
using (SqlCommand cmd = new SqlCommand([Ô]insert into clientes (Codigo, Nome, DataNascimento) values (@Codigo, @Nome, @DataNascimento)[Ô], cn)
{
Log();
cmd.Parameters.AddWithValue([Ô]@Codigo[Ô], this.Codigo);
cmd.Parameters.AddWithValue([Ô]@Nome[Ô], this.Nome);
cmd.Parameters.AddWithValue([Ô]@DataNascimento[Ô], this.DataNascimento);
cmd.ExecuteNonQuery();
}
}
}


Este é o comecinho do comecinho do início sobre Reflection. Tem muita coisa que se pode fazer usando essa técnica, combinada com outras, como por exemplo, Decorações, que torno a falar loguinho, por enquanto vou deixar só na curiosidade, mas para terem uma idéia, é possível fazer uma classe que será herdada por Cliente, que contém todos os métodos básicos de CRUD(Inserir, Deletar, Atualizar e Consultar) e na classe Cliente, ficam somente as propriedades de cliente. Uma vez essa classe pronta(a que contém os métodos de CRUD), ela pode ser herdada em qualquer outra entidade(Fornecedores por exemplo), e todos os métodos CRUD também estarão disponíveis para fornecedor, sem precisar escrever uma linha de código sequer. E mais, se precisar adicionar um campo(propriedade) em qualquer das classes, não precisa reescrever nenhum método do CRUD, ele vai [Ô]achar[Ô] a propriedade, montar a query pra você, executar, gravar o log e qualquer outra funcionalidade, também sem precisar mexer em uma linha sequer de código. Prático hein? Pois é...
FBGSYSTEMS 06/02/2014 12:08:57
#434356
Bom dia galera.
Estou pensando em fazer algo mas nao acho como.

Nas minhas expressoes SQL sempre tem um try catch para insercao, update e delete. Porem como indicado utilizo troca de parametros nestas expressoes.

Tem algum jeito de retornar o mysqlcommand ja com os parametros inseridos ali ?

Preciso disto pois quero gravar um log das operações.
KERPLUNK 06/02/2014 16:16:19
#434376
FBGSYSTEMS,

é o que vou explicar com o uso das decorações.
NILSONTRES 06/02/2014 17:22:49
#434385
Citação:

em algum jeito de retornar o mysqlcommand ja com os parametros inseridos ali ?

Preciso disto pois quero gravar um log das operações.



Coincidência, estava procurando exatamente isso.
KERPLUNK 06/02/2014 17:30:56
#434387
Citação:

Coincidência, estava procurando exatamente isso.


Então, vou dar um jeito de fazer o exemplo mais rápido, ando meio sem tempo, mas vou tentar apressar...
KERPLUNK 13/02/2014 11:26:35
#434715
Ok então, continuando com reflection com uso de decorações:
Na nossa classe Cliente, temos três propriedades, Nome, Idade e Data de Nascimento. Mais ou menos assim:

public class Cliente
{
public string Nome { get; set; }
public int Idade { get; set; }
public DateTime DataNascimento { get; set; }
}



Certo, simples e sem rodeios, uma classe simples. Cada uma das propriedades é uma característica da entidade(objeto) Cliente. Essa entidade, como já vimos, pode ser instanciada e valores podem ser atribuídos. Também métodos pode ser adicionados nela para trabalhar com ela. Vamos entender agora o conceito de decoração. Decoração(Decoration, em inglês), é nada mais que uma classe que posso atribuir à cada propriedade de cada entidade. Em outras palavras, são propriedades das propriedades. Em um outro arquivo(de preferência), vamos criar essa classe que vai servir de decoração para as propriedades do nosso objeto Cliente:

using System.Data;
public class Campo : Attribute
{
public string NomeDoCampo { get; set; }
public bool ChavePrimaria { get; set; }
public SqlDbType TipoSql { get; set; }
}


Reparem bem. O [Ô]using System.Data[Ô], possibilita usarmos uma propriedade com o tipo da classe [Ô]SqlDbType[Ô], que contém os tipos de dados do banco Sql Server. Além disso, reparem também que essa classe tem um [Ô] :Attribute[Ô], depois do nome dela. Isso é a herança. Essa classe está herdando de [Ô]Attribute[Ô], que é o que nos possibilita usarmos essa classe como uma decoração.
Além da classe [Ô]Campo[Ô], que descreve um campo de uma tabela, uma outra classe que vai ser necessária para nosso exemplo é uma classe para descrição de Tabela:

public class Tabela : Attribute
{
public string NomeTabela { get; set; }
}


Simples assim. Agora, vamos [Ô]decorar[Ô] nossa classe cliente com seus respectivos atributos:

[Tabela(NomeTabela=[Ô]Cliente[Ô])]
public class Cliente
{
[Campo(NomeDoCampo = [Ô]NomeCliente[Ô], TipoSql = SqlDbType.VarChar)]
public String Nome { get; set; }
[Campo(NomeDoCampo = [Ô]Idade[Ô], TipoSql = SqlDbType.Int)]
public int? Idade { get; set; }
[Campo(NomeDoCampo = [Ô]DtNascimento[Ô], TipoSql = SqlDbType.DateTime)]
public DateTime? DataNascimento { get; set; }
}


Ok, a nossa entidade está ficando um pouco mais complicada de se ler, mas com o tempo isso fica muito fácil se você usar isso com mais frequência.
Repare que cada propriedade, tem uma decoração [Ô]Campo[Ô], contendo o nome do campo, que é o nome do campo na tabela no banco de dados e seu respectivo tipo de dados, que vai ser necessário para as devidas conversões de valor. Repare também que os nomes de campos, não correspondem necessariamente aos nomes das suas respectivas propriedades, isso é normal e não tem problema nenhum, pois para formarmos as queries, vamos usar os nomes dos campos dessas decorações e não os nomes das propriedades na entidade.
Outro detalhe importante, é a mudança de tipo da propriedade [Ô]Nome[Ô] para String(em maiúsculo mesmo). A diferença é a possibilidade do uso de null nessa propriedade. Idade, também foi mudada para int?. Esse ponto de interrogação no final, especifica que essa propriedade recebe um valor int, mas também pode receber null, o mesmo caso para DateTime?. Experimente colocar string? para nome e veja o que acontece e entenderá porque mudei para String.

Certo, você já deve estar pensando: [Ô]Muito papo, mais ação! Quero ver como usar essas decorações![Ô]. Calma, já vamos chegar lá. Vamos fazer agora um método [Ô]Insere[Ô] na nossa classe cliente, que vai ficar mais ou menos assim:

public class Cliente
{
[Campo(NomeDoCampo = [Ô]NomeCliente[Ô], TipoSql = SqlDbType.VarChar)]
public String Nome { get; set; }
[Campo(NomeDoCampo = [Ô]Idade[Ô], TipoSql = SqlDbType.Int)]
public int? Idade { get; set; }
[Campo(NomeDoCampo = [Ô]DtNascimento[Ô], TipoSql = SqlDbType.DateTime)]
public DateTime? DataNascimento { get; set; }
public void Insere()
{
string Campos = [Ô][Ô];
string Valores = [Ô][Ô];
string NomeTabela = ((Tabela)this.GetType().GetCustomAttributes(true)[0]).NomeTabela;

using (SqlConnection cn = new SqlConnection([Ô][Ô]))
{
//cn.Open();
using (SqlCommand cmd = new SqlCommand([Ô]Insert Into {0}({1}) Values ({2})[Ô], cn))
{
foreach (PropertyInfo propriedade in this.GetType().GetProperties())
{
Campo decoracao = (Campo)propriedade.GetCustomAttributes(true)[0];
Campos += decoracao.NomeDoCampo +[Ô],[Ô];
Valores += [Ô]@[Ô] + decoracao.NomeDoCampo + [Ô],[Ô];
cmd.Parameters.Add(new SqlParameter()
{
ParameterName = [Ô]@[Ô] + decoracao.NomeDoCampo,
SqlDbType = decoracao.TipoSql,
Value = propriedade.GetValue(this, null)
});
}

Campos = Campos.Substring(0, Campos.Length - 1);
Valores = Valores.Substring(0, Valores.Length - 1);

//substituindo placeholders do comando com valores correspondentes
cmd.CommandText = string.Format(cmd.CommandText, NomeTabela, Campos, Valores);
}
}
}
}


Ufa! Que trabalheira hein? Um pouco sim, eu admito. Vamos analisar passo à passo esse método [Ô]Insere[Ô]:
Temos 3 variáveis internas, Campos, Valores e Tabela. Campos, conterá os nomes dos campos da tabela, que são os valores que estabeleci para cada propriedade de Cliente em suas respectivas decorações. Valores, conterá os valores da instância atual da classe e Tabela conterá o nome da tabela. Informação suficiente para uma Query [Ô]Insert Into[Ô], que está estabelecida usando placeholders, aqueles {0} {1} e {2}, em cada um deles, será colocado o valor correspondente, ou seja, a tabela, os campos e seus valores, que no caso, serão apenas placeholders de parâmetros, que por sua vez, são inseridos no comando [Ô]cmd[Ô], no loop dentro do bloco do comando. Repare, que em [Ô]Valores[Ô], adiciono mesmo somente o placeholder do valor, e logo abaixo, adiciono o parâmetro no comando, usando o mesmo nome do placeholder([Ô]@[Ô] + nome do campo) e o valor que a propriedade correspondente contiver. Abaixo do for...each, tenho dois comando, que servem simplesmente para tirar a última vírgula tanto de Valores como de Campos, para a sintaxe do SQL ficar correta. Tem outras maneiras mais elegantes de se fazer isso, mas assim achei mais simples para começarmos. A última linha, uso o comando com seus placeholders e substituo seus valores. Coloque um breakpoint nessa linha.

Para utilizarmos isso tudo, fica super simples, onde quer que você queira gravar o cliente, coloque:

Cliente cli = new Cliente();
cli.Nome = [Ô]Joãozinho[Ô];
cli.Idade = 14;
cli.DataNascimento = new DateTime(1986, 12, 15);
cli.Insere();


Se você adicionou o breakpoint na linha que indiquei acima e executar, o código vai parar nela e você pode verificar com a watch window, o comando contendo o nome da tabela, os campos, os placeholders de valores e todos os parâmetros adicionados, com todos os valores da instância [Ô]cli[Ô], do código logo acima, ou seja, o comando está pronto para ser executado. Agora, nesse código, ele não está sendo executado. Para isso, você deve criar uma tabela no seu banco de dados Sql, que tenha a estrutura da classe. Não esqueça, que os nomes dos campos dela, são os valores que estão em [Ô]NomeDoCampo[Ô], da decoração e não o nome da propriedade. O mesmo para os tipos de dados de cada campo e o nome da tabela. Então para testar, apenas coloque a string de conexão para seu banco onde [Ô]cn[Ô] está sendo instanciado e o comando [Ô]cmd.ExecuteNonQuery()[Ô], logo abaixo de onde fazemos a substituição dos placeholders no comando.

Com isso entendemos como utilizar as decorações de uma maneira produtiva. E ainda estamos começando hein. Conforme tiver um tempo, vou ensinar como construir, além do método de inserir, também os métodos de Atualizar(Update), Deletar(Delete) e buscar por chave primária. Depois, como colocamos tudo isso em uma única classe que será herdada por todas as suas entidades, tornando-as prontas para inserir, deletar, atualizar e procurar dados, sem precisar programar uma única linha à mais, desde que as decorações de cada propriedade de sua entidade estejam corretas, todas as suas entidades estarão funcionais, quase magicamente!

Dúvidas? Pergunta ae!
Até a próxima!
FBGSYSTEMS 13/02/2014 12:22:19
#434720
Caraca KERPLUNK, que muito doido isso aí !!.

No meu próximo sistema em C# vou tentar aplicar isso desde o começo.

Deu para entender certinho como funciona tudo. Só de pensar nas vantagens na economia de tempo.



Deixa eu te fazer uma pergunta, para o meu caso que o sistema está quase pronto e nao fiz nada desta forma que voce nos ensinou. Ainda existe alguma forma de retornar o comandtext depois da troca de parametros ? Nessa altura eu não conseguiria reescrever todo o sistema =(
NILSONTRES 13/02/2014 12:40:28
#434722
Parabens pelo Artigo KERPLUNK, muito louvável sua vontade de passar conhecimentos.

Estou no mesmo estagio do FBGSYSTEMS,

Citação:

Deixa eu te fazer uma pergunta, para o meu caso que o sistema está quase pronto e nao fiz nada desta forma que voce nos ensinou. Ainda existe alguma forma de retornar o comandtext depois da troca de parametros ?


KERPLUNK 13/02/2014 13:54:39
#434727
Citação:

:
Ainda existe alguma forma de retornar o comandtext depois da troca de parametros ? Nessa altura eu não conseguiria reescrever todo o sistema =(


Essa técnica funciona se você tiver colocado as decorações corretamente em todas as suas entidades. Mas não precisa ser tudo de uma vez, você pode fazer este [Ô]upgrade[Ô] parcialmente, uma entidade de cada vez. Além disso, esses métodos de CRUD(Inserir, deletar, alterar...) ficariam melhor se estivessem em uma classe separada, classe essa que é herdada em suas entidades. Essa super classe com métodos CRUD, também pode conter métodos de serialização/desserialização, log, um construtor de classe genérico que recebe como parâmetro um valor chave primária para iniciar a instância já com o objeto carregado, lazy loading, leitura automática para sub-entidades, enfim, pode ter uma infinidade de coisas.
Além disso, cada propriedade pode ter múltiplas decorações, isto é, pode ter decorações para outras funcionalidades. Mas nunca é tarde para deixar a casa em ordem e trabalhar da melhor maneira possível. Se precisar de ajuda, conte comigo!
Página 13 de 14 [136 registro(s)]
Faça seu login para responder