Sunday, December 25, 2016

Bab 7. Visual C# Untuk Programer



Pemrograman Generik






7.1 Pengantar

Bab ini mendiskusikan pemrograman generik, yang memberikan cara-cara dalam menciptakan model-model umum dalam pemrograman. Metode generik memampukan Anda untuk menetapkan, dengan satu deklarasi metode, sehimpunan metode yang saling berelasi. Kelas generik memampukan Anda untuk menetapkan, dengan satu deklarasi kelas, sehimpunan kelas yang saling berelasi. Sama juga, antarmuka generik memampukan Anda untuk menetapkan, dengan satu deklarasi antarmuka, sehimpunan antarmuka yang saling berelasi. Sampai sejauh ini, buku ini telah menggunakan tipe generik List dan Dictionary.

Anda dapat menuliskan sebuah metode generik untuk mengurutkan array yang memuat objek-objek, kemudian memanggil metode generik secara terpisah pada array int, array double, array string, dan seterusnya, untuk mengurutkan setiap array dengan tipe-tipe yang berbeda. Kompiler melakukan pemeriksaan tipe untuk memastikan bahwa array yang dilewatkan kepada metode pengurut hanya memuat elemen-elemen dengan tipe yang sesuai. Anda dapat menulis sebuah kelas generik Tumpukan yang memanipulasi setumpuk objek-objek, kemudian menginstansiasi objek-objek Tumpukan dengan tumpukan int, tumpukan double, tumpukan string, dan seterusnya. Kompiler melakukan pemeriksaan tipe untuk memastikan bahwa Tumpukan hanya memuat elemen-elemen dengan tipe yang sesuai.

7.2 Motivasi Menggunakan Metode Generik
Metode-metode teroverload seringkali dipakai untuk melakukan operasi-operasi yang sama pada data dengan tipe-tipe yang berbeda. Untuk memahami motivasi penggunaan metode generik, akan diberiksan sebuah contoh (Gambar 7.1) yang memuat tiga metode TampilArray teroverload (baris 23-29, baris 32-38), dan baris 41-47). Ketiga metode ini menampilkan elemen-elemen sebuah array int, array double, dan array char. Selanjutnya, nanti akan diimplementasikan-ulang program ini menggunakan satu metode generik.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// Gambar 7.1: Metode2Teroverload.cs
// Menggunakan metode-metode teroverload untuk menampilkan array-array berbeda tipe.
using System;

class Metode2Teroverload
{
    static void Main( string[] args )
    {
        // menciptakan array int, double, dan char
        int[] intArray = { 1, 2, 3, 4, 5, 6 };
        double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 };
        char[] charArray = { 'H', 'A', 'L', 'L', 'O' };

        Console.WriteLine( "Array intArray memuat:" );
        TampilArray( intArray ); // melewatkan sebuah argumen array int
        Console.WriteLine("Array doubleArray memuat:");
        TampilArray(doubleArray); // melewatkan sebuah argumen array double
        Console.WriteLine("Array charArray memuat:");
        TampilArray(charArray); // melewatkan sebuah argumen array char
    } // akhir Main

// menampilkan array int
private static void TampilArray( int[] arrayMasukan )
{
    foreach ( int elemen in arrayMasukan )
        Console.Write( elemen + " " );

    Console.WriteLine( "\n" );
} // akhir metode TampilArray

// menampilkan array array
private static void TampilArray( double[] arrayMasukan )
{
    foreach ( double elemen in arrayMasukan )
        Console.Write( elemen + " " );

    Console.WriteLine( "\n" );
} // akhir metode TampilArray

// menampilkan array char
private static void TampilArray( char[] arrayMasukan )
{
    foreach ( char elemen in arrayMasukan )
        Console.Write( elemen + " " );

    Console.WriteLine( "\n" );
} // akhir metode TampilArray
} // akhir kelas Metode2Teroverload

Array intArray memuat:
1 2 3 4 5 6

Array doubleArray memuat:
1.1 2.2 3.3 4.4 5.5 6.6 7.7

Array charArray memuat:
H A L L O

Program memulai dengan mendeklarasikan dan menginisialisasi tiga array, yaitu array int intArray enam-elemen  (baris 11), array double doubleArray tujuh-elemen (baris 11), dan array char charArray lima-elemen (baris 12). Kemudian, baris 14-19 menampilkan ketiga array itu.

Ketika kompiler menjumpai sebuah pemanggilan metode, ia akan mencoba mencari sebuah deklarasi metode yang memiliki nama sama dan parameter-parameter yang cocok dengan tipe-tipe argumen pada pemanggilan metode.

Pada contoh ini, setiap pemanggilan TampiArray cocok dengan salah satu deklarasi metode TampilArray. Sebagai contoh, baris 15 memanggil TampilArray dengan intArray sebagai argumennya. Pada saat kompilasi, kompiler menentukan tipe argumen intArray (misalnya, int[ ]), yang mencoba untuk menemukan sebuah metode yang bernama TampilArray yang menetapkan suatu parameter int[ ] (ditemukan pada baris 23-29) dan menetapkan pemanggilan terhadap metode tersebut. Sama halnya, ketika kompiler menjumpai pemanggilan TampilArray pada baris 17, ia menentukan tipe argume doubleArray (misalnya, double[ ]), kemudian mencoba mencari sebuah metode dengan nama TampilArray yang memiliki satu parameter double[ ] (ditemukan pada baris 32-38) dan menetapkan pemanggilan terhadap metode tersebut. Terakhir, ketika kompiler menjumpai pemanggilan array TampilArray pada baris 19, ia menentukan tipe argumen charArray (misalnya, char[ ]), kemudian mencoba mencari lokasi sebuah metode dengan nama TampilArray yang memiliki satu parameter char[ ] (ditemukan pada baris 41-47) dan menetapkan pemanggilan terhadap metode tersebut.

Anda bisa mencermati setiap metode TampilArray. Perhatikan bahwa tipe elemen array (int, double, atau char) muncul pada dua lokasi di dalam setiap metode, yaitu di header metode (baris 23, 32, dan 41) dan header statemen foreach (baris 25, 34, dan 43). Jika Anda mengganti tipe-tipe elemen pada tiap metode dengan sebuah nama generik (seperti T untuk “type”), maka ketiga metode akan menjadi seperti pada Gambar 7.2. Tampak bahwa Anda dapat mengganti tipe elemen array pada tiap metode dengan satu “parameter tipe generik”, kemudian Anda hanya perlu mendeklarasikan satu metode TampilArray yang menampilkan elemen-elemen dari sembarang array. Metode pada Gambar 7.2 tidak dapat dikompilasi, karena sintaksnya tidak tepat. Anda mendeklarasikan sebuah metode TampilArray generik yang sintaks yang tepat pada Gambar 7.3.

1
2
3
4
5
6
7
// Gambar 7.2: Deklarasi sebuah metode generik
private static void TampilArray( T[] arrayMasukan )
{
    foreach ( T elemen in arrayMasukan )
        Console.Write( elemen + " " );

    Console.WriteLine( "\n" );
} // akhir metode TampilArray

7.3 Implementasi Metode Generik
Jika operasi-operasi yang dilakukan oleh beberapa metode teroverload identik untuk tiap tipe argumen, maka metode-metode teroverload tersebut dapat dikode dengan lebih kompak menggunakan metode generik. Anda dapat menuliskan sebuah deklarasi metode generik yang dapat dipanggil dengan argumen-argumen berbeda tipe. Berdasarkan tipe-tipe argumen yang dilewatkan kepada metode generik, kompiler akan menangani setiap pemanggilan metode secara tepat.

Gambar 7.3 mengimplementasikan-ulang aplikasi pada Gambar 7.1 menggunakan sebuah metode TampilArray generik (baris 24-30). Perhatikan bahwa pemanggilan-pemanggilan metode TampilArray pada baris 6, 18, dan 20 identik dengan kode pada Gambar 7.1, yang menampilkan dua aplikasi identik dan kode pada Gambar 7.3 merupakan 17 baris lebih pendek dari kode pada Gambar 7.1. Seperti diilustrasikan pada Gambar 7.3, pemrograman generik memampukan Anda untuk menciptakan dan menguji kode sekali saja, kemudian menggunakan-kembali kode tersebut untuk pelbagai tipe data. Ini mendemonstrasikan kekuatan generik yang mengesankan.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Gambar 7.3: MetodeGenerik.cs
// Menggunakan metode-metode teroverload untuk menampilkan array-array pelbagai tipe.
using System;
using System.Collections.Generic;

class MetodeGenerik
{
    static void Main(string[] args)
    {
        // menciptakan array int, double, dan char
        int[] intArray = { 1, 2, 3, 4, 5, 6 };
        double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 };
        char[] charArray = { 'H', 'A', 'L', 'L', 'O' };

        Console.WriteLine("Array intArray memuat:");
        TampilArray(intArray); // melewatkan sebuah argumen array int
        Console.WriteLine("Array doubleArray memuat:");
        TampilArray(doubleArray); // melewatkan sebuah argumen array double
        Console.WriteLine("Array charArray memuat:");
        TampilArray(charArray); // melewatkan sebuah argumen array char
    } // akhir Main

// menampilkan array dengan semua tipe
private static void TampilArray<T>(T[] arrayMasukan)
{
    foreach (T elemen in arrayMasukan)
        Console.Write(elemen + " ");

    Console.WriteLine("\n");
} // akhir metode TampilArray
} // akhir kelas MetodeGenerik

Array intArray memuat:
1 2 3 4 5 6

Array doubleArray memuat:
1.1 2.2 3.3 4.4 5.5 6.6 7.7

Array charArray memuat:
H A L L O

Baris 24 memulai deklarasi dari metode TampilArray. Semua deklarasi metode generik memiliki daftar parameter-tipe yang diapit oleh sepasang kurung siku (<T> pada contoh ini) yang berada setelah nama metode. Setiap daftar parameter-tipe dapat memuat satu atau lebih parameter tipe, yang dipisahkan dengan koma. Parameter tipe merupakan sebuah pengenal yang dipakai menggantikan nama tipe aktual. Parameter tipe dapat digunakan untuk mendeklarasikan tipe nilai balik, tipe parameter, dan tipe variabel lokal di dalam deklarasi metode generik; parameter tipe berperan sebagai placeholder untuk argumen tipe yang merepresentasikan tipe data yang akan dilewatkan kepada metode generik.

Tubuh metode generik dideklarasikan seperti tubuh metode lainnya. Sebagai contoh, baris 26 mendeklarasikan elemen pada statemen foreach sebagai tipe T, yang cocok dengan parameter tipe (T) yang dideklarasikan pada baris 24. Selain itu, sebuah parameter tipe hanya dapat dideklarasikan sekali pada daftar parameter-tipe tetapi dapat muncul lebih dari sekali pada daftar parameter metode.

Daftar parameter-tipe pada metode TampilArray (baris 24) mendeklarasikan parameter tipe T sebagai placeholder untuk tipe elemen-array yang akan ditampilkan TampilAray. Perhatikan bahwa T muncul pada daftar parameter sebagai tipe elemen-array (baris 24). Header statemen foreach (baris 26) juga menggunakan T sebagai tipe elemen. Ada dua lokasi yang sama dimana metode-metode TampilArray teroverload dari Gambar 7.1 menetapkan tipe elemen int, double, atau char.

Sama seperti pada Gambar 7.1, program pada Gambar 7.3 memulai dengan mendeklarasikan dan menginisialisasi sebuah array int intArray enam-elemen (baris 11), sebuah array double doubleArray tujuh-elemen (baris 12), dan sebuah array char charArray lima-elemen (baris 13). Kemudian tiap array ditampilkan dengan memanggil TampilArray (baris 16, 18, dan 20), sekali dengan argumen intArray, sekali dengan argumen doubleArray, dan sekali dengan argumen charArray.

Pada kasus pada baris 16, kompiler menentukan bahwa kecocokan terbaik terjadi jika parameter tipe T pada baris 24 dan 26 dari deklarasi metode TampilArray diganti dengan tipe elemen pada argumen intArray (yaitu, int) pada pemanggilan metode. Kemudian, kompiler menetapkan sebuah pemanggilan terhadap TampilArray dengan int sebagai argumen tipe untuk parameter tipe T. Ini dikenal dengan penyimpulan-tipe. Proses yang sama diulangi untuk pemanggilan-pemanggilan terhadap metode TampilArray pada baris 18 dan 20.

Anda juga dapat menggunakan argumen tipe eksplisit untuk mengindikasikan bahwa tipe persis apa yang harus dipakai untuk memanggil sebuah fungsi generik. Sebagai contoh, baris 16 dapat dituliskan sebagai

TampilArray< int >( intArray ); // melewatkan sebuah argumen int

Pemanggilan metode tersebut secara eksplisit menyediakan argumen tipe (int) yang dipakai untuk mengganti parameter tipe T pada baris 24 dan 26 pada deklarasi metode TampilArray.

Untuk tiap variabel yang dideklarasikan dengan sebuah parameter tipe, kompiler juga menentukan apakah operasi-operasi yang dilakukan terhadap variabel semacam itu diijinkan untuk semua tipe yang diwakili oleh parameter tipe. Satu-satunya operasi pada elemen-elemen array dalam contoh ini adalah untuk menampilkan representasi string atas elemen-elemen tersebut. Baris 27 melakukan konversi boxing implisit untuk setiap elemen array tipe-nilai dan untuk pemanggilan ToString implisit pada tiap elemen array. Karena semua objek dapat memiliki metode ToString, kompiler menyimpulkan bahwa baris 27 merupakan operasi yang valid dilakukan terhadap setiap elemen array.

Dengan mendeklarasikan TampilArray sebagai sebuah metode generik pada Gambar 7.3, Anda dapat mengeliminasi kebutuhan akan metode-metode teroverload pada Gambar 7.1, menghemat 17 baris kode dan menciptakan sebuah metode yang dapat didaur-ulang yang dapat menampilkan representasi string atas tiap elemen pada semabarang array satu-dimensi, tidak hanya untuk array int, array double, atau array char.

7.4 Kekangan Tipe
Pada bagian ini, akan disajikan sebuah metode Maksimum generik yang menentukan menghasilkan elemen terbesar dari tiga argumennya (ketiganya dengan tipe sama). Metode generik pada contoh ini menggunakan parameter tipe untuk mendeklarasikan tipe nilai balik dan tiap parameter metode. Normalnya, ketika membandingkan nilai-nilai untuk menentukan mana yang lebih besar, Anda perlu menggunakan operator <. Namun, operator ini tidak dioverload agar bisa digunakan setiap tipe pada Pustaka Kelas .NET Framework. Kode generik tidak dapat menggunakan operasi ini. Jadi, ekspresi seperti variabel1 < variabel2 tidak diijinkan kecuali jika kompiler dapat memastikan bahwa operator < disediakan untuk setiap tipe yang akan digunakan pada kode generik. Sama halnya, Anda tidak dapat memanggil sebuah metode pada sebuah variabel tipe-generik kecuali jika kompiler dapat memastikan bahwa semua tipe yang akan digunakan pada kode generik tersebut mendukung metode itu.

Antarmuka IComparable<T>
Adalah memungkinkan untuk membandingkan dua objek bertipe sama jika tipe itu mengimplementasikan antarmuka generik IComparable<T> (dari namespace System). Keuntungan dari pengimplementasian antarmuka IComparable<T> adalah bahwa objek-objek IComparable<T> dapat dipakai dengan metode pengurutan dan metode pencarian dari kelas-kelas pada namespace System.Collections.Generic. Setiap struktur pada Pustaka Kelas .NET yang terkait dengan setiap tipe sederhana semuanya mengimplementasikan antarmuka ini. Sebagai contoh, struktur untuk tipe sederhana double adalah Double dan struktur untuk tipe sederhana int adalah Int32. Kedua Double dan Int32 mengimplementasikan antarmukan IComparable<T>. Tipe-tipe yang mengimplementasikan antarmuka IComparable<T> harus mendeklarasikan sebuah metode CompareTo untuk membandingkan objek-objek. Sebagai contoh, jika Anda memiliki dua int, int1 dan int2, maka keduanya dapat dibandingkan dengan ekspresi:

int1.CompareTo( int2 )

Metode CompareTo menghasilkan nilai balik 0 jika kedua objek sama, nilai balik negatif jika int1 lebih kecil daripada int2, atau nilai balik positif jika int1 lebih besar daripada int2. Adalah tanggung-jawab programer yang mendeklarasikan sebuah tipe yang mengimplementasikan antarmuka IComparable<T> untuk mendefinisikan metode CompareTo sehingga ia membandingkan isi dari dua objek dengan tipe tersebut dan menghasilkan hasil yang diinginkan.

Menetapkan Kekangan Tipe
Meskipun objek-objek IComparable dapat dibandingkan, objek-objek itu tidak dapat digunakan pada kode generik secara default, karena tidak semua tipe mengimplementasikan IComparable<T>. Namun, Anda dapat membatasi tipe-tipe yang dapat dipakai pada metode atau kelas generik untuk memastikan bahwa tipe-tipe itu memenuhi beberapa persyaratan. Fitur ini dikenal sebagai pengekangan tipe, untuk membatasi tipe argumen yang disediakan bagi parameter tipe tertentu. Gambar 7.4 mendeklarasikan metode Maksimum (baris 20-34) dengan sebuah kekangan tipe yang mensyaratkan setiap argumen pada metode harus bertipe IComparable<T>. Pembatasan ini penting, karena tidak semua objek dapat dibandingkan. Namun, semua objek IComparable<T> dijamin untuk memiliki sebuah metode CompareTo yang dapat dipakai pada metode Maksimum untuk menentukan argumen terbesar dari ketiga argumen yang ada.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Gambar 7.4: UjiMaksimum.cs
// Metode Maksimum generik menghasilkan yang terbesar dari ketiga objek.
using System;

class UjiMaksimum
{
    public static void Main( string[] args )
    {
        Console.WriteLine( "Maksimum dari {0}, {1} dan {2} adalah {3}\n",
            3, 4, 5, Maksimum(3, 4, 5));
        Console.WriteLine("Maksimum dari {0}, {1} dan {2} adalah {3}\n",
            6.6, 8.8, 7.7, Maksimum(6.6, 8.8, 7.7));
        Console.WriteLine("Maksimum dari {0}, {1} dan {2} adalah {3}\n",
            "mangga", "apel", "jeruk",
            Maksimum("mangga", "apel", "jeruk"));
    } // akhir Main

    // fungsi generik menentukan yang terbesar dari
    // objek-objek IComparable
    private static T Maksimum<T>(T x, T y, T z)
        where T : IComparable< T >
    {
        T maks = x; // mengasumsikan x awalnya terbesar

        // membandingkan y dengan maks
        if ( y.CompareTo( maks ) > 0 )
            maks = y; // y terbesar sejauh ini

        // membandingkan z dengan maks
        if ( z.CompareTo( maks ) > 0 )
            maks = z; // z adalah terbesar

        return maks; // menghasilka objek terbesar
    } // akhir metode Maksimum
} // akhir kelas UjiMaksimum

Maksimum dari 3, 4 dan 5 adalah 5

Maksimum dari 6.6, 8.8 dan 7.7 adalah 8.8

Maksimum dari mangga, apel dan jeruk adalah mangga

Metode Maksimum generik menggunakan parameter tipe T sebagai tipe nilai balik dari metode itu (baris 20), sebagai tipe dari parameter-parameter metode x, y, dan z (baris 20), dan sebagai tipe dari variabel lokal maks (baris 23). Klausa where pada metode Maksimum generik (setelah daftar parameter pada baris 21) menetapkan kekangan tipe untuk parameter tipe T. Pada kasus ini, klausa where T : IComparable<T> mengindikasikan bahwa metode ini memerlukan argumen tipe untuk mengimplementasikan antarmuka IComparable<T>. Jika tidak ada kekangan tipe yang ditetapkan, maka kekangan tipe default adalah object.

Metode Maksimum mengasumsikan bahwa argumen pertamanya (x) adalah yang terbesar dan menugaskannya kepada variabel lokal maks (baris 23). Selanjutnya, statemen if pada baris 26-27 menentukan apakah y lebih besar dari maks. Kondisi tersebut memanggil metode CompareTo pada y dengan ekspresi y.CompareTo(maks). Jika y lebih besar dari maks, maka y ditugaskan kepada variabel maks (baris 27). Sama halnya, statemen pada baris 30-31 menentukan apakah z lebih besar dari maks. Jika ya, maka baris 31 menugaskan z kepada maks. Kemudian, baris 33 menghasilkan maks dan memberikannya kepada pemanggil.

Pada Main (baris 7-16), baris 10 memangil Maksimum dengan integer 3, 4, dan 5. Metode Maksimum generik cocok dengan pemanggilan ini, tetapi argumennya harus mengimplementasikan antarmuka IComparable<T>. untuk memastikan bahwa argumen-argumen itu dapat dibandingkan. Tipe int merupakan sebuah sinonim untuk struct Int32, yang mengimplementasikan antarmuka IComparable<int>. Jadi, int (dan tipe-tipe sederhana lainnya) merupakan argumen yang valid untuk metode Maksimum.

Baris 12 melewatkan ketiga argumen double kepada Maksimum. Lagi, ini diijinkan karena double merupakan sebuah sinonim untuk struct Double, yang mengimplementasikan antarmuka IComparable<double>. Baris 15 melewatkan tiga string kepada Maksimum, yang juga merupakan objek-objek IComparable<string>.

7.5 Kelas Generik
Konsep sebuah struktur data (misalnya, tumpukan) yang memuat elemen-elemen data dapat dipahami secara bebas tanpa dipengaruhi oleh tipe elemen yang dimanipulasinya. Sebuah kelas generik menyediakan beberapa cara dalam menjelaskan suatu kelas dengan aspek bebas-tipe. Anda kemudian dapat menginstansiasi versi tipe spesifik dari kelas generik. Kapabilitas ini merupakan peluang bagi pendaur-ulangan kode.

Dengan kelas generik, Anda dapat menggunakan notasi sederhana dan singkat untuk mengindikasikan tipe aktual yang dipakai dalam menggantikan parameter tipe pada kelas. Pada saat kompilasi, kompiler memastikan keamanan tipe pada kode Anda dan mengganti parameter tipe dengan argumen tipe agar kode klien dapat berinteraksi dengan kelas generik tersebut.

Satu kelas Tumpukan generik, misalnya, dapat dipakai sebagai dasar untuk menciptakan banyak kelas Tumpukan lain (misalnya, Tumpukan dari double, Tumpukan dari int, Tumpukan dari char, Tumpukan dari Karyawan, dan lainnya). Gambar 7.5 menyajikan deklarasi kelas Tumpukan generik. Parameter tipe T merepresentasikan tipe elemen yang akan dimanipulasi oleh Tumpukan. Sama seperti metode generik, daftar parameter-tipe pada sebuah kelas generik dapat memuat satu atau lebih parameter tipe (yang masing-masing dipisahkan dengan koma). Parameter tipe T dipakai di keseluruhan deklarasi kelas Tumpukan (Gambar 7.5) untuk merepresentasikan tipe elemen. Kelas Tumpukan mendeklarasikan variabel elemen2 sebagai sebuah array bertipe T (baris 8). Array ini (diciptakan pada baris 21) akan menyimpan elemen-elemen Tumpukan.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Gambar 7.5: Tumpukan.cs
// Kelas Tumpukan generik.
using System;

class Tumpukan< T >
{
    private int atas; // lokasi dari elemen atas
    private T[] elemen2; // array yang memuat elemen-elemen tumpukan

    // konstruktor tanpa-parameter menciptakan tumpukan dengan ukuran default
    public Tumpukan()
        : this( 10 ) // ukuran tumpukan default
    {
        // konstruktor kosong;
    } // akhir konstruktor Tumpukan

    // konstruktor menciptakan sebuah tumpukan dengan jumlah elemen tertentu
    public Tumpukan( int ukuranTumpukan )
    {
        if (ukuranTumpukan > 0) // memvalidasi ukuranTumpukan
            elemen2 = new T[ukuranTumpukan]; // menciptakan ukuranTumpukan elemen
        else
            throw new ArgumentException( "Ukuran Tumpukan harus positif." );

        atas = -1; // tumpukan awalnya kosong
    } // akhir konstruktor tumpukan

    // menempatkan elemen ke atas tumpukan; jika tidak berhasil,
    // akan melemparkan eksepsi TumpukanPenuhException
    public void Push( T nilaiPush )
    {
        if ( atas == elemen2.Length - 1 ) // tumpukan penuh
            throw new TumpukanPenuhException( string.Format(
                "Tumpukan penuh, tidak bisa menempatkan {0} ke atas tumpukan", nilaiPush ) );

        ++atas; // menginkremen atas
        elemen2[ atas ] = nilaiPush; // menempatkan nilaiPush ke atas tumpukan
    } // akhir metode Push

    // menghasilkan elemen atas jika tidak kosong,
    // jika kosong, melempar eksepsi TumpukanKosongException
    public T Pop()
    {
        if ( atas == -1 ) // tumpukan kosong
            throw new TumpukanKosongException("Tumpukan kosong, tidak bisa operasi Pop");

        --atas; // mendekreman atas
        return elemen2[ atas + 1 ]; // menghasilkan nilai di atas tumpukan
    } // akhir metode Pop
} // akhir kelas Tumpukan

Kelas Tumpukan memiliki dua konstruktor. Konstruktor tanpa-parameter (baris 11-15) melewatkan ukuran tumpukan default (10) kepada konstruktor satu-argumen, menggunakan sintaks this (baris 12) untuk memanggil konstruktor lain pada kelas yang sama. Konstruktor satu-argumen (baris 18-26) memvalidasi argumen ukuranTumpukan dan menciptakan sebuah array dengan ukuranTumpukan yang ditetapkan (jika lebih besar dari 0) atau melemparkan sebuah eksepsi (jika lebih kecil dari 0).

Metode Push (baris 30-38) pertama-tama menentukan apakah yang dilakukan adalah mencoba untuk menempatkan sebuah elemen ke atas Tumpukan yang penuh. Jika ya, baris 33-34 akan melemparkan sebuah eksepsi TumpukanPenuhException (dideklarasikan pada Gambar 7.6). Jika Tumpukan tidak penuh, baris 36 akan menginkremen kounter atas untuk mengindikasikan posisi atas yang baru, dan baris 37 menempatkan argumen pada lokasi itu di dalam array elemen2.

Metode Pop (baris 42-49) pertama-tama menentukan apakah yang dilakukan adalah mencoba untuk menempatkan sebuah elemen ke atas Tumpukan yang penuh. Jika ya, baris 45 akan melemparkan sebuah eksepsi TumpukanKosongException (dideklarasikan pada Gambar 7.7). Sebaliknya, baris 47 akan mendekremen kounter atas untuk mengindikasikan posisi atas yang baru, dan baris 48 menghasilkan elemen atas yang asli pada Tumpukan.

Kelas TumpukanPenuhException (Gambar 7.6) dan TumpukanKosongException (Gambar 7.7) masing-masing menyediakan sebuah konstruktor tanpa-parameter, sebuah konstruktor satu-argumen, dan sebuah konstruktor dua-argumen untuk menciptakan sebuah eksepsi baru menggunakan kelas eksepsi yang sudah ada. Konstruktor tanpa-parameter menetapkan pesan error default sedangkan dua konstruktor yang lain menetapkan pesan error yang dapat dimodifikasi.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Gambar 7.6: TumpukanPenuhException.cs
// TumpukanPenuhException mengindikasikan bahwa tumpukan penuh.
using System;

class TumpukanPenuhException : Exception
{
    // konstruktor tanpa-parameter
    public TumpukanPenuhException() : base( "Tumpukan penuh" )
    {
        // konstruktor kosong
    } // akhir konstruktor TumpukanPenuhException

    // konstruktor satu-parameter
    public TumpukanPenuhException( string eksepsi ) : base( eksepsi )
    {
        // konstruktor kosong
    } // akhir konstruktor TumpukanPenuhException

    // konstruktor dua-parameter
    public TumpukanPenuhException(string eksepsi, Exception inner)
        : base(eksepsi, inner)
    {
        // konstruktor kosong
    } // akhir konstruktor TumpukanPenuhException
} // akhir kelas TumpukanPenuhException

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Gambar 7.7: TumpukanKosongException.cs
// TumpukanKosongException mengindikasikan bahwa tumpukan kosong.
using System;

class TumpukanKosongException : Exception
{
    // konstruktor tanpa-parameter
    public TumpukanKosongException() : base("Tumpukan kosong")
    {
        // konstruktor kosong
    } // akhir konstruktor TumpukanKosongException

    // konstruktor satu-parameter
    public TumpukanKosongException(string eksepsi) : base(eksepsi)
    {
        // konstruktor kosong
    } // akhir konstruktor TumpukanKosongException

    // konstruktor dua-parameter
    public TumpukanKosongException(string eksepsi, Exception inner)
        : base(eksepsi, inner)
    {
        // konstruktor kosong
    } // akhir konstruktor TumpukanKosongException
} // akhir kelas TumpukanKosongException

Sekarang, akan disajikan sebuah aplikasi (Gambar 7.8) yang menggunakan kelas Tumpukan generik. Baris 13-14 mendeklarasikan variabel-variabel bertipe Tumpukan<double> dan Tumpukan<int>. Tipe double dan int merupakan argumen tipe pada Tumpukan. Kompiler akan mengganti parameter-parameter tipe pada kelas generik sehingga ia dapat melakukan pemeriksaan tipe. Metode Main menginstansiasi objek-objek tumpukanDouble berukuran 5 (baris 18) dan tumpukanInt berukuran 10 (baris 19), kemudian memanggil metode UjiPushDouble (baris 28-48), UjiPopDouble (baris 51-73), UjiPushInt (baris 76-96), dan UjiPopInt (baris 99-121) untuk memanipulasi dua Tumpukan pada contoh ini.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Gambar 7.8: UjiTumpukan.cs
// Menguji kelas Tumpukan generik.
using System;

class UjiTumpukan
{
    // menciptakan array double dan array int
    private static double[] elemen2Double =
        new double[]{ 1.1, 2.2, 3.3, 4.4, 5.5, 6.6 };
    private static int[] elemen2Int =
        new int[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };

private static Tumpukan< double > tumpukanDouble; // menyimpan objek-objek double
private static Tumpukan<int> tumpukanInt; // menyimpan objek-objek int

    public static void Main( string[] args )
    {
        tumpukanDouble = new Tumpukan<double>(5); // tumpukan double
        tumpukanInt = new Tumpukan<int>(10); // tumpukan int

        UjiPushDouble(); // menempatkan double pada tumpukanDouble
        UjiPopDouble(); // menghapus double dari tumpukanDouble
        UjiPushInt(); // menempatkan int pada tumpukanInt
        UjiPopInt(); // menghapus int dari tumpukanInt
    } // akhir Main

    // menguji metode Push dengan tumpukanDouble
    private static void UjiPushDouble()
    {
        // menempatkan elemen-elemen ke atas tumpukan
        try
        {
            Console.WriteLine("\nMenempatkan elemen-elemen ke atas tumpukanDouble");

            // menempatkan elemen-elemen ke atas tumpukan
            foreach ( var elemen in elemen2Double )
            {
                Console.Write( "{0:F1} ", elemen );
                tumpukanDouble.Push( elemen ); // menempatkan ke atas tumpukanDouble
            } // akhir foreach
        } // akhir try
        catch ( TumpukanPenuhException eksepsi )
        {
            Console.Error.WriteLine();
            Console.Error.WriteLine( "Pesan: " + eksepsi.Message );
            Console.Error.WriteLine( eksepsi.StackTrace );
        } // akhir catch
    } // akhir metode UjiPushDouble

    // menguji metode Pop dengan tumpukanDouble
    private static void UjiPopDouble()
    {
        // menghapus elemen-elemen dari tumpukan
        try
        {
            Console.WriteLine("\nMenghapus elemen-elemen dari tumpukanDouble");

            double nilaiPop; // menyimpan elemen yang dihapus dari tumpukan

            // menghapus semua elemen dari tumpukan
            while ( true )
            {
                nilaiPop = tumpukanDouble.Pop(); // menghapus dari tumpukanDouble
                Console.Write( "{0:F1} ", nilaiPop );
            } // akhir while
        } // akhir try
        catch ( TumpukanKosongException eksepsi )
        {
            Console.Error.WriteLine();
            Console.Error.WriteLine( "Pesan: " + eksepsi.Message );
            Console.Error.WriteLine( eksepsi.StackTrace );
        } // akhir catch
    } // akhir metode UjiPopDouble

    // menguji metode Push dengan tumpukanInt
    private static void UjiPushInt()
    {
        // menempatkan elemen-elemen ke atas tumpukan
        try
        {
            Console.WriteLine( "\nMenempatkan elemen-elemen ke atas tumpukanInt" );

            // menempatkan elemen-elemen ke atas tumpukan
            foreach ( var elemen in elemen2Int )
            {
                Console.Write( "{0} ", elemen );
                tumpukanInt.Push( elemen ); // menempatkan ke atas tumpukanInt
            } // akhir foreach
        } // akhir try
        catch ( TumpukanPenuhException eksepsi )
        {
            Console.Error.WriteLine();
            Console.Error.WriteLine( "Pesan: " + eksepsi.Message );
            Console.Error.WriteLine( eksepsi.StackTrace );
        } // akhir catch
    } // akhir metode UjiPushInt

    // menguji metode Pop dengan tumpukanInt
    private static void UjiPopInt()
    {
        // menghapus elemen-elemen dari tumpukan
        try
        {
            Console.WriteLine( "\nMenghapus elemen-elemen dari tumpukanInt" );

            int nilaiPop; // menyimpan elemen yang dihapus dari tumpukan

            // menghapus semua elemen dari tumpukan
            while ( true )
            {
                nilaiPop = tumpukanInt.Pop(); // menghapus dari TumpukanInt
                Console.Write( "{0} ", nilaiPop );
            } // akhir while
        } // akhir try
        catch ( TumpukanKosongException eksepsi )
        {
            Console.Error.WriteLine();
            Console.Error.WriteLine( "Pesan: " + eksepsi.Message );
            Console.Error.WriteLine( eksepsi.StackTrace );
        } // akhir catch
    } // akhir metode UjiPopInt
} // akhir kelas UjiTumpukan

Menempatkan elemen-elemen ke atas tumpukanDouble
1.1 2.2 3.3 4.4 5.5 6.6
Pesan: Tumpukan penuh, tidak bisa menempatkan 6.6 ke atas tumpukan
   at Tumpukan`1.Push(T nilaiPush) in e:\Projects Visual C#\Tumpukan\
       Tumpukan\Program.cs:line 33
   at UjiTumpukan.UjiPushDouble() in e:\Projects Visual C#\Tumpukan\
   Tumpukan\UjiTumpukan.cs:line 39

Menghapus elemen-elemen dari tumpukanDouble
5.5 4.4 3.3 2.2 1.1
Pesan: Tumpukan kosong, tidak bisa melakukan operasi Pop
   at Tumpukan`1.Pop() in e:\Projects Visual C#\Tumpukan\
       Tumpukan\Program.cs:line 45
   at UjiTumpukan.UjiPopDouble() in e:\Projects Visual C#\Tumpukan\
       Tumpukan\UjiTumpukan.cs:line 63

Menempatkan elemen-elemen ke atas tumpukanInt
1 2 3 4 5 6 7 8 9 10 11
Pesan: Tumpukan penuh, tidak bisa menempatkan 11 ke atas tumpukan
   at Tumpukan`1.Push(T nilaiPush) in e:\Projects Visual C#\Tumpukan\
       Tumpukan\Program.cs:line 33
   at UjiTumpukan.UjiPushInt() in e:\Projects Visual C#\Tumpukan\
       Tumpukan\UjiTumpukan.cs:line 87

Menghapus elemen-elemen dari tumpukanInt
10 9 8 7 6 5 4 3 2 1
Pesan: Tumpukan kosong, tidak bisa melakukan operasi Pop
   at Tumpukan`1.Pop() in e:\Projects Visual C#\Tumpukan\
       Tumpukan\Program.cs:line 45
   at UjiTumpukan.UjiPopInt() in e:\Projects Visual C#\Tumpukan\
       Tumpukan\UjiTumpukan.cs:line 111

Metode UjiPushDouble (baris 28-48) memanggil metode Push untuk mengganti nilai-nilai double 1.1, 2.2, 3.3, 4.4 dan 5.5 yang disimpan di dalam array elemen2Double ke atas tumpukanDouble. Statemen foreach berhenti ketika program uji mencoba untuk menempatkan (Push) nilai keenam ke atas tumpukanDouble (yang penuh, karena tumpukanDouble hanya dapat menyimpan lima elemen). Pada kasus ini, metode ini melemparkan sebuah eksepsi TumpukanPenuhException (Gambar 7.6) untuk mengindikasikan bahwa Tumpukan penuh. Baris 42-47 menangkap eksepsi ini dan menampilkan pesan dan informasi jejak-tumpukan. Jejak tumpukan mengindikasikan eksepsi yang terjadi dan menampilkan bahwa metode Push pada kelas Tumpukan membangkitkan eksepsi pada baris 36 dari file Tumpukan.cs (Gambar 7.5). Jejak tersebut juga menunjukkan bahwa metode Push dipanggil oleh metode UjiPushDouble dari kelas UjiTumpukan pada baris 39 dari file UjiTumpukan.cs. Informasi ini memampukan Anda dalam menentukan metode-metode yang ada pada tumpukan pemanggilan-metode pada saat eksepsi terjadi.

Metode UjiPopDouble (baris 51-73) memanggil metode Pop pada kelas Tumpukan menggunakan sebuah loop while untuk menghapus semua nilai dari tumpukan. Perhatikan pada keluaran bahwa nilai-nilai yang dihapus dalam urutan LIFO (last-in, first-out). Ini merupakan karakteristik dari struktur data tumpukan. Loop while (baris 61-65) berlanjut sampai tumpukan kosong. Eksepsi TumpukanKosongException terjadi ketika program mencoba melakukan operasi penghapus pada tumpukan kosong. Ini menyebabkan program melompat ke blok catch (baris 67-72) dan menangani eksepsi tersebut, sehingga program dapat melanjutkan eksekusi. Ketika program uji mencoba melakukan operasi Pop untuk menghapus nilai keenam, tumpukanDouble telah kosong, sehingga metode Pop melemparkan eksepsi TumpukanKosongException.

Metode UjiPushInt (baris 76-96) memanggil metode Push pada kelas Tumpukan untuk menempatkan nilai-nilai ke atas tumpukanInt sampai ia penuh. Metode UjiPopInt (baris 99-121) memanggil metode Pop pada kelas Tumpukan untuk menghapus nilai-nilai dari tumpukanInt sampai ia kosong. Sekali lagi, perhatikan bahwa nilai-nilai dihapus dari tumpukan dalam tatanan LIFO.

Menciptakan Metode Generik untuk Menguji Kelas Tumpukan<T>
Perhatikan bahwa kode pada metode UjiPushDouble dan UjiPushInt hampir identik untuk menempatkan nilai-nilai ke atas Tumpukan<double> atau Tumpukan<int>. Sama halnya, kode pada metode UjiPopDouble dan UjiPopInt hampir identik untuk menghapus nilai-nilai dari atas Tumpukan<double> atau Tumpukan<int>. Hal ini menciptakan peluang untuk memanfaatkan metode generik. Gambar 7.9 mendeklarasikan metode UjiPush (baris 33-54) untuk melakukan pekerjaan yang sama dengan UjiPushDouble dan UjiPushInt pada Gambar 7.8. Sama halnya, Gambar 7.9 mendeklarasikan metode UjiPop (baris 57-79) untuk melakukan pekerjaan yang sama dengan UjiPopDouble dan UjiPopInt pada Gambar 7.8.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// Gambar 7.9: UjiTumpukanMetodeGenerik.cs
// Menguji kelas Tumpukan generik.
using System;
using System.Collections.Generic;

class UjiTumpukanMetodeGenerik
    {
        // menciptakan array double dan array int
        private static double[] elemen3Double =
            new double[] { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6 };
        private static int[] elemen3Int =
            new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };

        private static Tumpukan<double> tumpukanDouble1; // menyimpan objek-objek double
        private static Tumpukan<int> tumpukanInt1; // menyimpan objek-objek int

        public static void Main(string[] args)
        {
            tumpukanDouble1 = new Tumpukan<double>(5); // tumpukan double
            tumpukanInt1 = new Tumpukan<int>(10); // tumpukan int

            // menempatkan double-double pada tumpukanDouble1
            UjiPush("tumpukanDouble1", tumpukanDouble1, elemen3Double);
            // menghapus double-double dari tumpukanDouble1
            UjiPop("tumpukanDouble1", tumpukanDouble1);
            // menempatkan int-int pada tumpukanInt1
            UjiPush("tumpukanInt1", tumpukanInt1, elemen3Int);
            // menghapus int-int dari tumpukanInt1
            UjiPop("tumpukanInt1", tumpukanInt1);
        } // akhir Main

        // menguji metode Push
private static void UjiPush<T>(string nama, Tumpukan<T> tumpukan,
    IEnumerable<T> elemen2)
        {
            // menempatkan elemen-elemen ke atas tumpukan
            try
            {
                Console.WriteLine("\nMenempatkan elemen-elemen ke atas " + nama);

                // menempatkan elemen-elemen ke atas tumpukan
                foreach (var elemen in elemen2)
                {
                    Console.Write("{0} ", elemen);
                    tumpukan.Push(elemen); // menempatkan ke atas tumpukan
                } // akhir foreach
            } // akhir try
            catch (TumpukanPenuhException eksepsi)
            {
                Console.Error.WriteLine();
                Console.Error.WriteLine("Pesan: " + eksepsi.Message);
                Console.Error.WriteLine(eksepsi.StackTrace);
            } // akhir catch
        } // akhir metode UjiPush

        // menguji metode Pop dengan tumpukanDouble
        private static void UjiPop<T>(string nama, Tumpukan<T> tumpukan)
        {
            // menghapus elemen-elemen dari tumpukan
            try
            {
                Console.WriteLine("\nMenghapus elemen-elemen dari " + nama);

                T nilaiPop; // menyimpan elemen yang dihapus dari tumpukan

                // menghapus semua elemen dari tumpukan
                while (true)
                {
                    nilaiPop = tumpukan.Pop(); // menghapus dari tumpukan
                    Console.Write("{0} ", nilaiPop);
                } // akhir while
            } // akhir try
            catch (TumpukanKosongException eksepsi)
            {
                Console.Error.WriteLine();
                Console.Error.WriteLine("Pesan: " + eksepsi.Message);
                Console.Error.WriteLine(eksepsi.StackTrace);
            } // akhir catch
        } // akhir metode UjiPop
    } // akhir kelas UjiTumpukanMetodeGenerik

Menempatkan elemen-elemen ke atas tumpukanDouble1
1.1 2.2 3.3 4.4 5.5 6.6
Pesan: Tumpukan penuh, tidak bisa menempatkan 6.6 ke atas tumpukan
   at Tumpukan`1.Push(T nilaiPush) in e:\Projects Visual C#\Tumpukan\
       Tumpukan\Program.cs:line 33
   at UjiTumpukanMetodeGenerik.UjiPush[T](String nama, Tumpukan`1 tumpukan,
       IEnumerable`1 elemen2) in e:\Projects Visual C#\Tumpukan\
       Tumpukan\UjiTumpukanMetodeGenerik.cs:line 45

Menghapus elemen-elemen dari tumpukanDouble1
5.5 4.4 3.3 2.2 1.1
Pesan: Tumpukan kosong, tidak bisa melakukan operasi Pop
   at Tumpukan`1.Pop() in e:\Projects Visual C#\Tumpukan\
       Tumpukan\Program.cs:line 45
   at UjiTumpukanMetodeGenerik.UjiPop[T](String nama, Tumpukan`1 tumpukan)
       in e:\ Projects Visual C#\Tumpukan\Tumpukan\
      UjiTumpukanMetodeGenerik.cs:line 69

Menempatkan elemen-elemen ke atas tumpukanInt1
1 2 3 4 5 6 7 8 9 10 11
Pesan: Tumpukan penuh, tidak bisa menempatkan 11 ke atas tumpukan
   at Tumpukan`1.Push(T nilaiPush) in e:\Projects Visual C#\Tumpukan\
       Tumpukan\Program.cs:line 33
   at UjiTumpukanMetodeGenerik.UjiPush[T](String nama, Tumpukan`1 tumpukan,
       IEnumerable`1 elemen2) in e:\Projects Visual C#\Tumpukan\
       Tumpukan\UjiTumpukanMetodeGenerik.cs:line 45

Menghapus elemen-elemen dari tumpukanInt1
10 9 8 7 6 5 4 3 2 1
Pesan: Tumpukan kosong, tidak bisa melakukan operasi Pop
   at Tumpukan`1.Pop() in e:\Projects Visual C#\Tumpukan\
       Tumpukan\Program.cs:line 45
   at UjiTumpukanMetodeGenerik.UjiPop[T](String nama, Tumpukan`1 tumpukan)
       in e:\Projects Visual C#\Tumpukan\Tumpukan\
       UjiTumpukanMetodeGenerik.cs:line 69

Metode Main (baris 17-30) menciptakan Tumpukan<double> pada baris 19 dan Tumpukan<int> (baris 20). Baris 23-29 memanggil metode generik, UjiPush dan UjiPop, untuk menguji objek-objek Tumpukan.

Metode UjiPush generik (baris 33-54) menggunakan parameter tipe T (ditetapkan pada baris 33) untuk merepresentasikan tipe data yang disimpan di dalam Tumpukan. Metode generik itu mengambil tiga argumen, yaitu sebuah string yang merepresentasikan nama dari objek Tumpukan, sebuah objek bertipe Tumpukan<T>, dan sebuah IEnumerable<T> yang memuat elemen-elemen yang akan ditempatkan ke atas Tumpukan<T>. Perhatikan bahwa kompiler menegakkan konsistensi antara tipe dari Tumpukan dan elemen-elemen yang akan ditempatkan ke atas Tumpukan ketika Push dipanggil. Metode UjiPop generik (baris 57-79) memerlukan dua argumen, yaitu sebuah string yang merepresentasikan nama dari objek Tumpukan dan sebuah objek bertipe Tumpukan<T>.


No comments:

Post a Comment