ORIENTACAO A OBJETOS - CONCEITO

FFCOUTO 07/05/2013 11:05:36
#423042
Amigos,

Hoje mantenho um sistema desenvolvido em VB6 para empresa onde trabalho. Como todos sabem, inevitavelmente, chega a hora de migrar para ferramentas atuais, e no meu caso, essa hora chegou.

Depois de muita leitura e tentativa de entendimento da OOP e a utilização de camadas iniciei o novo projeto que será escrito em C# (ainda tenho de me familiarizar).

Minha preocupação não é com a linguagem, mas com o conceito que será abordado, pois no futuro tenho planos de levar o sistema para o ambiente Web. Hoje a necessidade ainda é por uso nos desktops.

Com o exposto acima, gostaria de pedir ao colegas que pudessem avaliar neste pequeno exemplo que eu disponibilizo aqui, se estou no caminho certo e se a aplicação do conceito OOP está correto, se não, onde tenho de melhorar.

Grato a todos pela ajuda.

P.S. O exemplo acima utilizei a mesma classe de clientes que estou usando no VB6 hoje.

OCELOT 07/05/2013 11:43:20
#423043
Eu diria que se você quer trabalhar bem com orientação a objetos e futuramente poder reusar boa parte do seu código em uma plicação Web minha recomendação seria estudar algum design pattern como o MVC, MVP ou MVVM, além de procurar usar algum componente que faça o ORM (Object-Relational Mapping) como o Entity Framework ou NHibernate (eu uso o XPO da DevExpress) pois assim você poupa o trabalho de ter que fazer isso manualmente.

De preferencia eu diria para você estudar um dos design patterns para saber como funcionam e procurar um framework para facilitar o trabalho com ele, apesar de não ser necessário pode te ajudar a começar.

KERPLUNK 07/05/2013 12:13:55
#423045
Citação:

:
Eu diria que se você quer trabalhar bem com orientação a objetos e futuramente poder reusar boa parte do seu código em uma plicação Web minha recomendação seria estudar algum design pattern como o MVC, MVP ou MVVM, além de procurar usar algum componente que faça o ORM (Object-Relational Mapping) como o Entity Framework ou NHibernate (eu uso o XPO da DevExpress) pois assim você poupa o trabalho de ter que fazer isso manualmente.

De preferencia eu diria para você estudar um dos design patterns para saber como funcionam e procurar um framework para facilitar o trabalho com ele, apesar de não ser necessário pode te ajudar a começar.



Essa é uma faca de dois gumes, muita gente, [Ô]não se acerta[Ô] com ORM[ô]s e prefere fazer as coisas à mão, mas isso depende muito do caso. Eu, particularmente, prefiro o uso de ORM[ô]s sempre que possível. Vou dar uma olhadinha no seu projeto e retorno com o que eu achar.
LLAIA 07/05/2013 13:30:29
#423047
Estou lendo este livro, e é muito legal:

http://blog.lambda3.com.br/2008/12/livro-de-ddd-do-evans-de-graca-na-web/

Preocupação no domínio do sistema e no controle do modelo. Acho que à medida que vc vai aprofundando nos padrões POO, dá pra ir acompanhando esse livro.
KERPLUNK 07/05/2013 16:03:35
#423055
Estive olhando o seu projeto e não está de todo mal, algumas coisas à acrescentar:
1 - Utilize uma classe virtual ou uma interface para padronizar os objetos do CRUD
2 - Procure usar métodos genéricos para transformação de DataReader em objeto. Nesse caso, o uso de atributos nas propriedades pode vir à calhar
3 - Construa uma classe factory para sua conexão e evite usar uma propriedade ou field nas classes. De preferência singleton, para [Ô]aproveitar[Ô] uma conexão já aberta. E também de preferência que a string de conexão seja montada ou mesmo buscada de um arquivo XML que também de de preferência, deve estar encriptado.
4 - Faça uso das suas próprias exceptions ao invés de somente pegar exception genéricas, por exemplo, ao selecionar um cliente usando código como parâmetro, caso o DataReader não contenha registros, dispare uma exception [Ô]ClienteNaoEncontrado[Ô]
5 - Eu não costumo separar camadas em projetos diferentes, mas isso é opinão pessoal. Já tive problemas com isso, do tipo uma DLL estar compilada com versão anterior da outra.
6 - Se você não vai fazer uso interno das propriedades, não tem porque elas terem um field. A declaração genérica da mesma já bastaria, como:
public string Nome {get; set;}

7 - As entidades poderiam conter construtores que recebem como parâmetro um valor de chave primária, assim, pode-se instanciar um objeto de forma bem simples:

Cliente cli = new Cliente(999); //999 poderia ser o valor vindo de uma textbox, por exemplo

8 - (Minha opinião) métodos de inserção, exclusão e alteração, são na grande maioria das vezes referentes ao tipo(objeto), e é mais simples usar esses métodos na própria entidade:

Cliente cli = new Cliente(899);
cli.Excluir();

//ou

Cliente cli = new Cliente(999);
cli.Nome = [Ô]outra coisa qualquer[Ô];
cli.Update();

9 - Para os métodos de seleção, você poderia usar uma lista de parâmetros, ficando mais ou menos assim:

ParametroCliente par1 = new ParametroCliente();
par1.Valor = [Ô]josé[Ô];
par1.Campo = ParametroClienteEnum.Nome;
par1.Operador = OperadoresEnum.Parecido;

ParametroCliente par2 = new ParametroCliente();
par2.Valor = [Ô]Cangaíba[Ô];
par2.Campo = ParametroClienteEnum.Bairro;
par2.Operador = OperadoresEnum.Igual;

List<PararmetroCliente> parametrosPesquisa = new List<Parrametrocliente>();
parametrosPesquisa.Add(par1);
parametrosPesquisa.Add(par2);

List<Cliente> clientesSelecionados = DALCliente().Selecionar(parapetrosPesquisa);

10 - Atenção aos campos nullables do seu banco de dados. Se o campo no banco pode conter null, a propriedade referente à esse campo na sua entidade também deve poder receber null.


Essas seriam algumas sugestões, tenho mais algumas, mas primeiro vamos ver o que você acha destas...
FFCOUTO 07/05/2013 19:44:50
#423070
Amigos, desde já agradeço pela atenção e pelo tempo despendido com este tópico.

OCELOT
Eu dei uma pesquisa sobre os Designer Patterns, mas de nada adianta usar “assistentes” para realizar uma tarefa que pode ser feita manualmente. Além do mais, a melhor forma de aprender é fazer tudo até para familiarizar os conceitos.

LLAIA
Vou baixar o livro e ler com muita atenção. Valeu.

KERPLUNK
1. Teria um exemplo deste ponto. Pois imaginei uma classe estática com os métodos apenas executando os comandos no banco, sem validação pois esta já seria feita na camada BLL.
2. Você diz respeito quando da leitura do recordset? Como seria feito isso?
3. Hoje no meu aplicativo tenho uma classe Database com métodos básicos do tipo abrir e fechar conexão, iniciar e finalizar transação, etc. Quanto a string, eu já uso um arquivo de configuração criptografado em AES 256 bits. Recupero os valores das propriedades e eu monto, apenas as propriedades usuário e senha eu acrescento na hora do login pois não utilizo uma tabela de usuário e sim os objetos de login do próprio SQL Server. De qualquer maneira teria um exemplo.
4. Isso já é feito hoje, não me preocupei neste exemplo pois o meu intuito é a aprendizagem do conceito OOP. Fique tranqüilo.
5. Então eu poderia ter apenas uma .dll com as camadas BLL e DAL? Pois por toda a minha pesquisa sempre foi dito que o melhor é a separação, mas enfim, também prefiro uma .dll só.
6. Realmente não sabia dessa possibilidade. Aprendi um coisa nova.
7. Concordo. Apenas não fiz por ser um exemplo, mas todas as entidades (classes) terão um ou mais construtores devido a campos que são somente leitura e devem ser iniciados. Pois no VB6 eu usava um método Friend para fazer essas passagens.
8. Aqui também estou de acordo. Contudo, nas minhas pesquisas sempre vi essa separação, achei por melhor utilizar as técnicas aprendidas. Mas concordo que a Alteração e Exclusão poderiam ser feitas dentro da classe. A inserção acho que poderia ser feita fora (passando os parametros requeridos) para que o retorno fosse uma nova classe instanciada. O que acha?
9. Isso realmente é uma boa idéia, não havia pensado por este ângulo.
10. Os únicos campos no meu banco que recebe valor null são os campos datetime. Campos numéricos quando não precisam de valor é inserido 0 e os textos são inseridos vazio (“”).

Resumindo: Dos quatro projetos posso diminuir para dois. Ficando a UI num projeto e BLL e DAL em outro. Vou remontar os projetos aqui e postar novamente para avaliação.

Muito obrigado a todos pela ajuda
KERPLUNK 08/05/2013 09:37:11
#423104
1 - Bem o assunto da herança é bem extenso, mas pode começar vendo aqui um pouco mais a fundo.
2 - Sim, quanto à leitura do DataReader. Cada propriedade conterá atributos, que não são nada mais do que uma outra classe que herda de Attribute:

//em uma classe separada, preferencialmente

namespace Utils //ou qualquer outro nome que faça sentido
{
public class Campo : Attribute
{
public string NomeCampo {get; set;}
public int Tamanho { get; set; }
}
}


//Na sua classe:
using Utils;
public class Cliente
{
[Campo(NomeCampo=[Ô]NOME_CLIENTE[Ô], Tamanho = 100)]
public string Nome {get; set;}
}

Observe no exemplo acima, que logo acima da propriedade nome, consta um atributo que pode ser facilmente acessado por reflection, que vai ser o que você vai usar para buscar o nome do campo especificamente. Então a leitura se dará mais ou menos assim:

using (SqlConnection cn = DBFactory.GetInstance())
{
using (SqlCommand cmd = new SqlCommand([Ô]Select * from YYY[Ô], cn))
{
using (SqlDataReader dr = cmd.ExecuteReader())
{
List<Cliente> retorno = ConvertDrToList<Cliente>(dr); //Aqui a leitura é feita
}
}
}

O método [Ô]ConvertDrToList[Ô], vai converter o DataReader inteiro em uma lista de objetos <Cliente>, lendo registro por registro, campo a campo e para cada campo do registro, ele verifica todas as propriedades da classe Cliente, procura a propriedade em que o atributo [Ô]Campo.NomeCampo[Ô], seja igual ao nome do campo sendo lido e atribui o valor. Essa mesma técnica pode ser usada para montar os comandos de Insert, Delete e Update.
A vantagem disso, é que se você precisar, digamos inserir mais um campo na tabela(e consequentemente na classe), basta inserir o campo na tabela e criar a propriedade equivalente, todos os métodos de leitura, gravação e exclusão, automaticamente já estarão usando o campo, eliminando a necessidade de alterar vários métodos e correr o risco de fazer um errado ou esquecer de fazer.
LUIS.HERRERA 08/05/2013 10:41:35
#423106
Kerplunk não entendi direito (desculpem aproveitar o post, mas o assunto é interessante).

Tenho a Classe Entidade com a estrutura da minha tabela ex:
//tabela Clientes

namespace MeuApp.Modelos
{
public class Cliente
{ //uso assim pois no VS 2008 não suporta pubic int ID {get; set; }
private int _ID;
public int ID
{
get { return _ID; }
set { _ID = value; }
}
private string _NOME;
public int NOME
{
get { return _NOME; }
set { _NOME= value; }
}
private byte _INATIVO;
public int INATIVO
{
get { return _INATIVO; }
set { _INATIVO= value; }
}

// etc.. para demais campos
}
}


Ai tenho a Classe DAL (com insert, select, etc...e BLL

1) Como eu faria essa parte dos campos (atributos), não seria dentro da classe modelo? Então deveria ter uma outra classe antes da modelo para dentro da modelo eu incluir o que você disse: [Campo(NomeCampo=[Ô]NOME_CLIENTE[Ô], Tamanho = 100)]. Isso deveria ser feito para cada campo da classe Cliente e quanto aos tipos diferentes: string, date, byte, int, etc...?

2) essa parte ficaria dentro da DAL?
using (SqlConnection cn = DBFactory.GetInstance())
{
using (SqlCommand cmd = new SqlCommand([Ô]Select * from YYY[Ô], cn))
{
using (SqlDataReader dr = cmd.ExecuteReader())
{
List<Cliente> retorno = ConvertDrToList<Cliente>(dr); //Aqui a leitura é feita
}
}
}


aqui fiquei confuso também. Um exemplo de update com duas tabelas relacionadas na DAL.
     public void Alterar(AtitudeModelo atitudeID, List<Atitude_IdiomaModelo> atitudeIdioma)
{
SqlTransaction transacao = null;
int registrosAfetados = 0;
try
{
string sql = [Ô]UPDATE ATITUDES SET INATIVO = @INATIVO WHERE (ID = @ID);[Ô];
SqlCommand cmd = new SqlCommand(sql, Dados.cn);

Dados.AbrirConexao();
transacao = Dados.cn.BeginTransaction();
cmd.Transaction = transacao;

// parâmetros do comando (campos da tabela)
cmd.Parameters.Add([Ô]@ID[Ô], SqlDbType.Int);
cmd.Parameters[[Ô]@ID[Ô]].Value = atitudeID.ID;
cmd.Parameters.Add([Ô]@INATIVO[Ô], SqlDbType.Bit);
cmd.Parameters[[Ô]@INATIVO[Ô]].Value = atitudeID.INATIVO;
cmd.ExecuteNonQuery();

string sqlIdiomas = [Ô]UPDATE ATITUDE_IDIOMA SET ATITUDE = @ATITUDE WHERE ((IDATITUDE = @IDATITUDE) AND (IDIOMA = @IDIOMA));[Ô];
cmd.CommandText = sqlIdiomas;

//-> Inclui a atitude em cada idioma informado na tabela auxiliar
foreach (Atitude_IdiomaModelo idiomaAtual in atitudeIdioma)
{
cmd.CommandText = sqlIdiomas;
cmd.Parameters.Clear();
cmd.Parameters.Add([Ô]@IDATITUDE[Ô], SqlDbType.Int);
cmd.Parameters[[Ô]@IDATITUDE[Ô]].Value = atitudeID.ID;
cmd.Parameters.Add([Ô]@ATITUDE[Ô], SqlDbType.NVarChar);
cmd.Parameters[[Ô]@ATITUDE[Ô]].Value = idiomaAtual.ATITUDE;
cmd.Parameters.Add([Ô]@IDIOMA[Ô], SqlDbType.NChar, 2);
cmd.Parameters[[Ô]@IDIOMA[Ô]].Value = idiomaAtual.IDIOMA;
registrosAfetados = cmd.ExecuteNonQuery();
if (registrosAfetados == 0)
{ // tem que incluir, pois ainda não há esse item com este idioma
cmd.CommandText = [Ô]INSERT INTO ATITUDE_IDIOMA (IDATITUDE,ATITUDE,IDIOMA) VALUES (@IDATITUDE,@ATITUDE,@IDIOMA);[Ô];
cmd.ExecuteNonQuery();
}
}
transacao.Commit();
}
catch (SqlException)
{
transacao.Rollback();
throw;
}
catch (Exception)
{
transacao.Rollback();
throw;
}
finally
{
Dados.FecharConexao();
}
}


Nota: tenho cadastros que ainda irei montar que terão +/- 10 tabelas auxiliares para um único registro no Insert / update.
KERPLUNK 08/05/2013 11:37:33
#423109
LUIS HERRERA,

1 - Imagine os atributos como se fosse [Ô]propriedades das propriedades[Ô], ou seja, cada propriedade vai ter suas próprias propriedades, acessíveis por meio de reflection. E sim, deve ser colocado para cada propriedade da sua classe, se quiser fazer uso do reflection. O tipo de dados é irrelevante, pois com o uso de reflection, você pode buscar o valor com o tipo object e fazer uso do método ChangeType para o tipo que a propriedade for, método esse também genérico. Eu faço isso o tempo todo e não tem problema algum, inclusive tratando valores null. Se quiserem posso postar um exemplo completo disso.

2 - Sim, esse é um fragmento de código que seria usado para leitura de dados dentro de algum método de busca.
LUIS.HERRERA 08/05/2013 11:52:21
#423112
Seria muito útil sim um exemplo.
FFCOUTO 08/05/2013 11:57:20
#423113
LUIS
Não precisa se desculpar, quanto mais informação aqui melhor.

KERPLUNK
1. A parte da herança eu compreendi. Inclusive já fiz aqui. Muito interesante.
2. Aqui eu encontrei um problema pra mim. Quando a propriedade da classe for readonly pois é o resultado de um cálculo por exemplo, como ficaria.
3- De onde vem o método [Ô]ConvertDrToList[Ô], pois não o encontrei. é um método que você criou? Se sim, poderia postar um exemplo.

Grato.
Página 1 de 14 [136 registro(s)]
Faça seu login para responder