Quando olho para trás lembro-me de quando eu era um humilde padawan que queria ser um monge Jedi em Java, e lembro claramente dos conselheiros Jedi do RSJUG falando "vá pelo caminho da orientação a objetos".
Se passaram alguns muitos anos e eu ainda lembro e sou fiel a esse princípio, e quando vejo por aí classes ou entidades de banco de dados, que eu irei ter que transformar em classes¹, contendo 181 campos (não é piada!) minha gastrite quase me mata!
Eu não sei por que as pessoas teimam em ter campos como CPF, CNPJ, data de inicio e de fim, etc em certas classes.
A quem vai ficar destinada a tarefa de validar um CNPJ/CPF? Ou se a data de início não é maior do que a de fim? Não tenha receio de criar classes que não serão persistidas. As pessoas tem a imagem de que classes de domínio/modelo só devem existir se forem ser persistidas, o resto é classe útil, DAO, service, etc. e não é bem por aí.
É do domínio da minha aplicação um CNPJ/CPF! E eu tenho que ter um objeto para isso, com a responsabilidade de validar se ele mesmo é válido. Não vou jogar essa responsabilidade para uma classe megazord no pacote útil responsável por validar isso e, talvez, mais coisas em um único lugar (olha o acoplamento aíííííííí gente).
Bem, para exemplificar isso, vou mostrar um código/abordagem que gosto muito, onde eu separo o campo nome da classe usuário em um objeto a parte.
Se você parar para pensar, o nome deve conter sempre 3 campos: primeiro nome, inicial do meio e último(s) nome(s).
Mas e o usuário SISTEMA? Ele não tem sobrenome. E o John Doe que não tem nome do meio?
Então eu tenho a classe PersonName:
using System;
using System.Text;
namespace com.blogspot.jeanjmichel.model
{
public class PersonName
{
private String firstName;
private String middleInitial;
private String lastName;
public String FirstName
{
set { this.firstName = value; }
get { return this.firstName; }
}
public String MiddleInitial
{
set { this.middleInitial = value; }
get { return this.middleInitial; }
}
public String LastName
{
set { this.lastName = value; }
get { return this.lastName; }
}
public PersonName()
{
}
public PersonName(String firstName)
{
this.FirstName = firstName;
}
public PersonName(String firstName, String middleInitial,
String lastName)
{
this.FirstName = firstName;
this.MiddleInitial = middleInitial;
this.LastName = lastName;
}
public override String ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append(this.LastName);
sb.Append(", ");
sb.Append(this.FirstName);
sb.Append(" ");
sb.Append(this.MiddleInitial);
return sb.ToString();
}
}
}
E agora a classe User:
using System;
using System.Text;
namespace com.blogspot.jeanjmichel.model
{
public class User
{
#region attributes
Int64 id;
String username;
PersonName personName;
char gender;
String password;
char active;
#endregion
#region properties
public Int64 Id
{
set { this.id = value; }
get { return this.id; }
}
public String Username
{
set { this.username = value; }
get { return this.username; }
}
public PersonName PersonName
{
set { this.personName = value; }
get { return this.personName; }
}
public char Gender
{
set { this.gender = value; }
get { return this.gender; }
}
public String Password
{
set { this.password = value; }
get { return this.password; }
}
public char Active
{
set { this.active = value; }
get { return this.active; }
}
#endregion
/// <summary>
/// Returns a string with this instance's data
/// The format of the string is:
/// namespace + class name + [ + the fields most representative
/// of the class + ].
/// </summary>
/// <returns>Returns a string with this instance's data</returns>
public override String ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("com.blogspot.jeanjmichel.model.User [");
sb.Append(this.PersonName);
sb.Append(" as '");
sb.Append(this.Username);
sb.Append("'");
sb.Append(" Active: ");
sb.Append(this.Active);
sb.Append("]");
return sb.ToString();
}
public User()
{
}
public User(String username, PersonName personName, String password,
char active)
{
this.Username = username;
this.PersonName = personName;
this.Password = password;
this.Active = active;
}
public User(Int64 id, String username, PersonName personName,
char gender, String password, char active)
{
this.Id = id;
this.Username = username;
this.PersonName = personName;
this.Gender = gender;
this.Password = password;
this.Active = active;
}
}
}
Agora criando um usuário:
using System;
using com.blogspot.jeanjmichel.model;
namespace com.blogspot.jeanjmichel
{
class Program
{
static void Main(string[] args)
{
PersonName pn = new PersonName("Jean", "J", "Michel");
User u = new User("jmichel", pn, "Senha", 'Y');
Console.WriteLine(u);
Console.ReadKey();
}
}
}
A saída é: com.blogspot.jeanjmichel.model.User [Michel, Jean J as 'jmichel' Active: Y].
Quase tudo funcionou.
Mas no teste unitário quando eu digo que criando o usuário System assim:
using System;
using com.blogspot.jeanjmichel.model;
namespace com.blogspot.jeanjmichel
{
class Program
{
static void Main(string[] args)
{
PersonName pn = new PersonName("System");
User u = new User("sysadmin", pn, "Senha", 'Y');
Console.WriteLine(u);
Console.ReadKey();
}
}
}
Eu espero o resultado: com.blogspot.jeanjmichel.model.User [System as 'sysadmin' Active: Y] o que acontece?
Failed TestUserToStringMethod TestProject Assert.AreEqual failed.
Expected:<com.blogspot.jeanjmichel.model.User [System as 'sysadmin' Active: Y]>.
Actual..:<com.blogspot.jeanjmichel.model.User [, System as 'sysadmin' Active: Y]>.
Holly crap! Então, a resposabilidade da minha classe PersonName é validar esse tipo de coisa. Refatorando-a teremos:
using System;
using System.Text;
namespace com.blogspot.jeanjmichel.model
{
public class PersonName
{
private String firstName;
private String middleInitial;
private String lastName;
public String FirstName
{
set { this.firstName = value; }
get { return this.firstName; }
}
public String MiddleInitial
{
set { this.middleInitial = value; }
get { return this.middleInitial; }
}
public String LastName
{
set { this.lastName = value; }
get { return this.lastName; }
}
public PersonName()
{
}
public PersonName(String firstName)
{
this.FirstName = firstName;
}
public PersonName(String firstName, String middleInitial,
String lastName)
{
this.FirstName = firstName;
this.MiddleInitial = middleInitial;
this.LastName = lastName;
}
public override String ToString()
{
StringBuilder sb = new StringBuilder();
if (String.IsNullOrEmpty(this.LastName))
{
sb.Append(this.FirstName);
}
else
{
sb.Append(this.LastName);
sb.Append(", ");
sb.Append(this.FirstName);
sb.Append(String.IsNullOrEmpty(this.MiddleInitial) ? "" :
" " + this.MiddleInitial);
}
return sb.ToString();
}
}
}
Eu faço o mesmo com CNPJ/CPF, datas (eu crio uma classe chamada período) e outras.
¹ Eu não criei uma classe com 181 campos, mas também não pude refatorar a classe, a tabela, etc (software de terceiros). então criei uma classe com o set mínimo de informações da tabela, que deu mais de 30 campos, e faço o enxerto do resto com os valores padrão para cada campo. Uma pena eu não poder quebrar essa maldita tabela em meia dúzia de 8 tabelas mais produtivas e reutilizáveis. Enfim, coisas da TI que o McGyver aprovaria :)
quarta-feira, outubro 23, 2013
Não deixe o básico da orientação a objetos de lado, nunca!
Marcadores:
.net,
acoplamento,
análise orientada a objetos,
c#,
java,
lo,
orientação a objetos,
responsabilidade
Assinar:
Postar comentários (Atom)
Nenhum comentário:
Postar um comentário