Hoje trago aos leitores deste
blog um artigo enviado pelo meu amigo Samir Lohmann. Samir é analista de sistemas
e desenvolvedor .NET, com larga experiência no desenvolvimento de sistemas. Neste artigo Samir traz um exemplo prático da
utilização de Linq, e conta como esta ferramenta o ajudou a encontrar uma
solução elegante, simples e muito eficiente em uma rotina que escreveu. Segue o artigo:
Quando a
Microsoft lançou o Linq (Language Integrated Query) no Framework 3.5, foi essa
a pergunta que eu me fiz. Tudo que ele faz, consigo fazer de outras maneiras.
Seria só mais uma novidade inútil adicionada ao .NET para justificar a
compra/upgrade para novas versões?
Não perdi muito
tempo com esses dilemas, e ignorei o Linq por um bom tempo.
Entretanto, no
meu último projeto, tive um bom tempo para refatorar, ou seja, aperfeiçoar o
código mantendo a mesma funcionalidade interna, e comecei a mudar a minha
opinião sobre esse recurso da plataforma.
Para demonstrar
como o Linq me ajudou, vou recorrer a um exemplo. Eu tinha as seguintes
classes:
''' <summary>
''' Representa um item de chapa de aço, lido do
sistema XXX
''' </summary>
''' <remarks></remarks>
Public Class
StockItem
Public Codigo As
String
Public Material As String
Public Espessura As Decimal
Public Comprimento As
Decimal
Public Largura As Decimal
Public Sub New(ByVal pCodigo As String, ByVal pMaterial As String, ByVal
pEspessura As Decimal,
ByVal pComprimento As
Decimal, ByVal
pLargura As Decimal)
Codigo =
pCodigo
Material =
pMaterial
Espessura =
pEspessura
Comprimento =
pComprimento
Largura =
pLargura
End Sub
End Class
''' <summary>
''' Representa o estoque disponível para corte
das peças de um trabalho
''' </summary>
''' <remarks></remarks>
Public Class
Stock
Inherits List(Of
StockItem)
End Class
Agora, vamos adicionar
manualmente alguns itens nessa lista, para efeito deste exemplo:
Private Sub
Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles
MyBase.Load
Dim s As New Stock()
s.Add(New
StockItem("0001", "Aço Carbono SAE 1020", 9.5, 6000,
2000))
s.Add(New
StockItem("0002", "Aço Carbono SAE 1045", 4.75, 6000,
2440))
s.Add(New
StockItem("0003", "Aço Inox", 10, 6000, 2440))
s.Add(New
StockItem("0004", "Aço Carbono SAE 1020", 9.5, 5000, 2000))
s.Add(New
StockItem("0005", "Aço Carbono SAE 1045", 4.75, 3000,
1500))
s.Add(New
StockItem("0006", "Aço Carbono SAE 1045", 4.75, 3200,
1200))
End Sub
Problema:
Na classe Stock, eu precisava de uma lista de materiais agrupados por
Material e Espessura, ou seja, uma lista como esta:
1: Material Aço Carbono SAE 1020,
Espessura 9.5 mm -> 2 itens
2: Material Aço Carbono SAE 1045,
Espessura 4.75 mm -> 3 itens
3: Material Aço Inox, Espessura
10 mm -> 1 item
Não me interessava realmente
quantos itens havia em cada grupo, e sim, quais grupos existiam.
Como funcionava na primeira
versão:
Não vale a pena colocar o código
aqui. Mas eu precisava de outra classe (apenas com os atributos Material e
Espessura) e precisava criar uma lista de objetos dessa classe. Para populá-la,
eu corria a lista de itens e, para cada um, verificava se a minha lista
agrupada já continha aquele item agrupado, se sim, eu adicionava outro item na
lista. Isso tudo dava umas 70 linhas de código VB.Net.
Como ficou na versão refatorada:
Tive que adicionar 1 (UMA) linha
na minha classe Stock e pude apagar
as 70 originais:
''' <summary>
''' Representa o estoque disponível para corte
das peças de um trabalho
''' </summary>
''' <remarks></remarks>
Public Class
Stock
Inherits List(Of
StockItem)
Public MateriaisAgrupados = From
si In Me Group By Material =
si.Material, Espessura = si.Espessura Into grp
= Group Select
Material, Espessura, grp
End Class
Embora a sintaxe seja uma espécie
de SQL com os componentes em ordem trocada, o que confunde um pouco o
entendimento do código, eu consegui substituir 70 linhas por uma, com mais
elegância e eficiência (já chego à parte da eficiência).
Vamos analisar cada parte do novo
código:
Public MateriaisAgrupados =
Esta será a minha lista. Notem
que ela não tem um tipo explícito. O tipo dela será definido pelo .NET como:
{System.Linq.Enumerable.WhereSelectEnumerableIterator(Of
<anonymous type>, <anonymous type>)}
From si In Me
“Me” é a classe atual que, no
caso, é uma lista. Estou dizendo que vou fazer um select na lista Me, e
cada item dela é um si (StockItem para mim).
Group By Material = si.Material,
Espessura = si.Espessura Into grp = Group
No select, estou fazendo
um agrupamento pelos atributos Material e Espessura de StockItem, e definindo alias
para os atributos agrupados que vou usar no select.
Ainda, estou definindo um grupo com os StockItem originais, que estou chamando
de grp (grupo).
Select Material, Espessura, grp
Por
fim, seleciono Material, Espessura e grp definidos anteriormente.
O
resultado, ao consultar minha lista, é este:
O
mais interessante de tudo, é que eu não apenas tenho o agrupamento, mas também
tenho uma lista (grp) dos itens originais que geraram esse grupo!
Muito
mais elegante que o código original, e muito mais rápido de desenvolver. E,
provavelmente, mais eficiente também, pois:
1.
A forma como a
equipe de desenvolvedores da MS implementou o agrupamento deve ser melhor que a
minha (eu espero!)
2.
A lista só é
realmente gerada quando eu a acesso. Ou seja, o meu atributo MateriaisAgrupados
não é propriamente uma lista estática, ela só é criada quando eu a acesso (e
eventualmente, posso não precisar dela). Com isso, também tenho a certeza de
ter a lista sempre atualizada.
O
loop nessa lista também funciona com item de tipo anônimo:
For Each
it In s.MateriaisAgrupados
Debug.Write(String.Format("Material
{0} Espessura {1} mm {2}", it.Material, it.Espessura, vbCrLf))
Next
Observação:
Para
alguém que, como eu, praticamente aprendeu a programar a sério em C,
essa história de tipos anônimos não soa muito simpática. Mas, com os recursos
computacionais de hoje em dia, um tipo anônimo aqui ou ali não será uma
desgraça para a a aplicação.
Conclusão:
Linq não é só uma modinha da Microsoft!
Abraços a todos, e um agradecimento especial ao Samir, pela
colaboração.
0 comentários:
Postar um comentário