;

segunda-feira, 2 de julho de 2012

Linq... qual é a utilidade mesmo?


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