Melihat LINQ Di .NET Dari Sudut Pandang Groovy


LINQ (Language-Integrated Query) dieja menyerupai ‘link’ adalah sebuah fitur unik di .NET Framework yang berkaitan dengan lambda expression. LINQ diperkenalkan sejak .NET Framework 3.5 dan Visual Studio 2008. Fitur ini dapat dipakai di bahasa untuk .NET Framework seperti C# dan Visual Basic. Pada kesempatan ini, saya akan mencoba memakai LINQ di C#. Karena fitur seperti ini tidak ada di Java 7 (yang belum mendukung lambda expression), maka saya akan mencoba membandingkannya dengan fitur serupa di Groovy.

Satu hal yang perlu disadari dari awal adalah LINQ hanyalah sebuah spesifikasi atau API. Programmer yang memakai LINQ harus menggunakan salah satu provider yang mengimplementasikan LINQ. Pada C# terdapat beberapa provider LINQ seperti LINQ to Objects, LINQ to XML, LINQ to SQL, dan LINQ to DataSets. Bila programmer merasa ini tidak cukup, ia boleh saja membuat provider baru yang menimplementasikan interface yang disediakan oleh LINQ. Pada artikel ini, saya akan memakai provider LINQ to Objects.

Saya akan mulai dengan membuat sebuah class, misalnya class ItemPenjualan yang isinya seperti berikut ini:

class ItemPenjualan
{
    public string NamaBarang { get; set; }

    public int Jumlah { get; set; }

    public decimal Harga { get; set; }

    public decimal Total()
    {
        return Jumlah * Harga;
    }
}

Class di atas mewakili setiap baris dalam faktur. Tentunya baris-baris faktur itu tidak dapat berdiri sendiri bila tidak ditampung oleh sebuah class yang mewakili faktur, misalnya Penjualan, seperti berikut ini:

class Penjualan
{
    private List<ItemPenjualan> listItemPenjualan;

    public Penjualan()
    {
        DiskonPersen = 0;
        listItemPenjualan = new List<ItemPenjualan>();
    }

    public string Nomor { get; set; }

    public DateTime Tanggal { get; set; }

    public string Konsumen { get; set; }

    public decimal DiskonPersen { get; set; }

    public List<ItemPenjualan> ListItemPenjualan
    {
        get
        {
            return listItemPenjualan;
        }
    }

    public void TambahItemPenjualan(ItemPenjualan itemPenjualan)
    {
        listItemPenjualan.Add(itemPenjualan);
    }

    public decimal Total()
    {
        decimal total = ListItemPenjualan.Sum(i => i.Total());
        return total - (DiskonPersen / 100 * total);
    }

    public override string ToString()
    {
        return string.Format("Nomor: {0}, Tanggal: {1}, Konsumen: {2}, Total: {3}",
            Nomor, Tanggal.ToString("dd-MM-yyyy"), Konsumen, Total());
    }
}

Sekarang, pada Program.cs (definisi class utama), saya mendeklarasikan beberapa object dari kedua class di atas, seperti berikut ini:

class Program
{
    static void Main(string[] args)
    {
        Penjualan p1 = new Penjualan() { Nomor = "F001", Konsumen = "Snake", Tanggal = new DateTime(2014, 1, 1) };
        p1.TambahItemPenjualan(new ItemPenjualan() { NamaBarang = "SOCOM Pistol", Jumlah = 1, Harga = 10 });
        p1.TambahItemPenjualan(new ItemPenjualan() { NamaBarang = "ID Card", Jumlah = 2, Harga = 20 });
        p1.TambahItemPenjualan(new ItemPenjualan() { NamaBarang = "Optical Disc", Jumlah = 1, Harga = 50 });            

        Penjualan p2 = new Penjualan() { Nomor = "F002", Konsumen = "Liquid Snake", Tanggal = new DateTime(2014, 1, 5) };
        p2.TambahItemPenjualan(new ItemPenjualan() { NamaBarang = "Ration", Jumlah = 10, Harga = 5 });
        p2.TambahItemPenjualan(new ItemPenjualan() { NamaBarang = "Stealth Camouflage", Jumlah = 1, Harga = 300 });
        p2.DiskonPersen = 5;

        Penjualan p3 = new Penjualan() { Nomor = "F003", Konsumen = "Big Boss", Tanggal = new DateTime(2014, 3, 15) };
        p3.TambahItemPenjualan(new ItemPenjualan() { NamaBarang = "Thermal Goggles", Jumlah = 1, Harga = 100 });
        p3.TambahItemPenjualan(new ItemPenjualan() { NamaBarang = "Night Vision Goggles", Jumlah = 1, Harga = 120 });
        p3.TambahItemPenjualan(new ItemPenjualan() { NamaBarang = "Body Armour", Jumlah = 20, Harga = 10 });
        p3.TambahItemPenjualan(new ItemPenjualan() { NamaBarang = "Cigarettes", Jumlah = 100, Harga = 2 });
        p3.DiskonPersen = 10.5M;

        List<Penjualan> semuaPenjualan = new List<Penjualan>() { p1, p2, p3 };

    }
}

Pada kode program di atas, terdapat tiga (3) object Penjualan yaitu p1, p2, p3. Ketiga object tersebut ditampung dalam sebuah List yang diberi nama semuaPenjualan. Pada kasus nyata, List akan diperoleh dari berbagai sumber atau setelah melewati berbagai pengolahan. Tapi yang pasti, setelah diperoleh, List tersebut perlu dipakai untuk menjawab pertanyaan, bukan?

Sebagai contoh, saya ingin mengetahui Penjualan apa saja yang terjadi di bulan Januari 2014. Saya dapat melakukannya dengan memanggil method LINQ seperti berikut ini:

var hasilQuery = semuaPenjualan.Where(p => p.Tanggal.Month == 1 && p.Tanggal.Year == 2014);

foreach (var p in hasilQuery)
{
    Console.WriteLine(p);
}

Output dari kode program di atas adalah:

Nomor: F001, Tanggal: 01-01-2014, Konsumen: Snake, Total: 100
Nomor: F002, Tanggal: 05-01-2014, Konsumen: Liquid Snake, Total: 332,50

Hal ini tidak dapat dilakukan di Java. Tapi ini adalah hal biasa di Groovy! Sebagai contoh, pada Groovy, saya dapat membuat kode program seperti berikut ini:

import groovy.transform.ToString
import java.text.SimpleDateFormat

@ToString
class ItemPenjualan {

    String namaBarang

    Integer jumlah

    BigDecimal harga

    public BigDecimal total() {
        jumlah * harga;
    }

}

@ToString(excludes = 'listItemPenjualan')
class Penjualan {

    String nomor

    Date tanggal

    String konsumen

    Double diskonPersen = 0

    List<ItemPenjualan> listItemPenjualan = []

    void tambahItemPenjualan(ItemPenjualan itemPenjualan) {
        listItemPenjualan << itemPenjualan;
    }

    BigDecimal total() {
        def total = listItemPenjualan.sum { it.total() }
        total - (diskonPersen / 100 * total)
    }

}

SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy")

Penjualan p1 = new Penjualan(nomor: "F001", konsumen: "Snake", tanggal: df.parse("01-01-2014"))
p1.tambahItemPenjualan(new ItemPenjualan(namaBarang: "SOCOM Pistol", jumlah: 1, harga: 10))
p1.tambahItemPenjualan(new ItemPenjualan(namaBarang: "ID Card", jumlah: 2, harga: 20))
p1.tambahItemPenjualan(new ItemPenjualan(namaBarang: "Optical Disc", jumlah: 1, harga: 50))

Penjualan p2 = new Penjualan(nomor: "F002", konsumen: "Liquid Snake", tanggal: df.parse("05-01-2014"))
p2.tambahItemPenjualan(new ItemPenjualan(namaBarang: "Ration", jumlah: 10, harga: 5))
p2.tambahItemPenjualan(new ItemPenjualan(namaBarang: "Stealth Camouflage", jumlah: 1, harga: 300))
p2.diskonPersen = 5

Penjualan p3 = new Penjualan(nomor: "F003", konsumen: "Big Boss", tanggal: df.parse("15-02-2014"))
p3.tambahItemPenjualan(new ItemPenjualan(namaBarang: "Thermal Goggles", jumlah: 1, harga: 100))
p3.tambahItemPenjualan(new ItemPenjualan(namaBarang: "Night Vision Goggles", jumlah: 1, harga: 120))
p3.tambahItemPenjualan(new ItemPenjualan(namaBarang: "Body Armour", jumlah: 20, harga: 10))
p3.tambahItemPenjualan(new ItemPenjualan(namaBarang: "Cigarettes", jumlah: 100, harga: 2))
p3.diskonPersen = 10.5

List<Penjualan> semuaPenjualan = [p1, p2, p3]

Calendar c = Calendar.getInstance()

semuaPenjualan.findAll {
    c.setTime(it.tanggal)
    c.get(Calendar.MONTH)==0 && c.get(Calendar.YEAR)==2014
}.each {
    println it
}

Walaupun kode program untuk penanganan tanggal di Groovy terlihat lebih kompleks (itu sebabnya saya selalu memakai Joda Time dan ini akan diperbaiki di Java 8 nanti), pada dasarnya proses query di Groovy hampir sama seperti pada penggunaan LINQ.

Tapi ada satu hal yang membuat LINQ unik. Ia juga dapat dipakai dalam bentuk syntax seperti SQL. Berbeda dengan SQL yang kerap diwakili sebuah string, syntax LINQ merupakan bagian dari syntax bahasa. Sebagai contoh, saya dapat menulis ulang versi LINQ sebelumnya menjadi seperti kode program C# berikut ini:

var hasilQuery = from p in semuaPenjualan
                 where 
                    p.Tanggal.Month == 1 && 
                    p.Tanggal.Year == 2014
                 select p                     
                 ;

foreach (var p in hasilQuery)
{
    Console.WriteLine(p);
}

Keyword from, where dan select pada LINQ di atas adalah bagian dari struktur bahasa C#. Microsoft menyarankan agar LINQ dipakai melalui syntax seperti di atas. Walaupun demikian, saya sering merasa penggunaan syntax LINQ seperti di atas membuat kode program terlihat lebih ‘gendut’.

Saya tidak dapat melakukan hal serupa di Groovy. Yang paling mendekati adalah dengan membuat DSL (domain specific language) sendiri seperti yang saya lakukan di simple-jpa, tapi saya tetap terbatas pada syntax dan semantic Groovy.

LINQ terlihat agak mirip SQL, bukan? Sebagai contoh, berikut ini adalah kode program C# yang menghitung total penjualan di bulan Januari 2014:

decimal totalJanuari =
    (from p in semuaPenjualan
     where
        p.Tanggal.Month == 1 &&
        p.Tanggal.Year == 2014
     select p).Sum(p => p.Total());   

Console.WriteLine("Total penjualan Januari 2014 adalah {0}", totalJanuari);

Kode program di atas setera dengan kode program Groovy berikut ini:

def totalJanuari = semuaPenjualan.findAll {
        c.setTime(it.tanggal)
        c.get(Calendar.MONTH)==0 && c.get(Calendar.YEAR)==2014
    }.sum { it.total() }
println "Total penjualan Januari 2014 adalah $totalJanuari"

Bila saya ingin mengetahui total penjualan per bulan, saya dapat menggunakan LINQ seperti pada kode program C# berikut ini:

var hasilQuery = from p in semuaPenjualan
                 group p by p.Tanggal.Month into groupBulan
                 select new { bulan = groupBulan.Key, total = groupBulan.Sum(p => p.Total()) }
                 ;

foreach (var p in hasilQuery)
{
    Console.WriteLine(p);
}

Hasil dari kode program C# di atas akan terlihat seperti berikut ini:

{ bulan = 1, total = 432,50 }
{ bulan = 3, total = 554,900 }

Karena Groovy tidak memiliki fitur serupa, yang paling mendekati adalah kode program seperti berikut ini:

def hasil = semuaPenjualan.groupBy {
    c.setTime(it.tanggal)
    c.get(Calendar.MONTH)
}.collect {
    new Expando(bulan: it.key, total: it.value.sum { it.total() })
}

println hasil

Hasil dari kode program Groovy di atas adalah:

[{bulan=0, total=432.5}, {bulan=1, total=554.8999999999999772626324556767940521240234375}]

Bagi yang sudah terbiasa dengan syntax SQL, kode program C# yang memakai LINQ akan terlihat lebih mudah dipahami.

Perihal Solid Snake
I'm nothing...

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: