Mengatur Alur Eksekusi Dengan Percabangan if Di C#

Mengatur Alur Eksekusi Dengan Percabangan if Di C#



Pada materi kali ini, kita akan mempelajari bagaimana mengatur alur eksekusi sebuah program dengan percabangan if di C#.

Setelah selesai mempelajari materi ini, Anda akan mendapatkan pemahaman tentang bagaimana mengatur alur eksekusi sebuah program menggunakan konstruksi percabangan if di C#. Selain itu, Anda juga akan mengenal berbagai operator yang bisa digunakan untuk mengevaluasi sebuah kondisi di dalam pernyataan if.

Alur Eksekusi Program

Seringkali sebuah aplikasi perlu mengeksekusi baris kode program tertentu atau bahkan mengabaikannya sama sekali tergantung dari apa kondisi yang terpenuhi.

Misalnya sebuah aplikasi, di mana pengguna diharuskan melakukan login dengan memasukkan email dan kata sandi, terlebih dahulu perlu mengevaluasi apakah data yang dimasukkan telah sesuai dengan data yang dimiliki oleh aplikasi tersebut atau tidak. Jika sesuai, maka pengguna tersebut akan diberi akses untuk menggunakan aplikasi tersebut. Sebaliknya jika tidak sesuai, maka pengguna tersebut akan ditolak aksesnya sekaligus diberi peringatan bahwa data masukan dari pengguna tidak valid.

Mengatur alur eksekusi seperti demikian, bisa direalisasikan baik dengan menggunakan percabangan if maupun percabangan switch. Percabangan if di C# akan dipelajari di materi ini, sementara percabangan switch akan kita pelajari di materi berikutnya.

Meskipun konsep ini cukup sederhana, namun memahami dan menguasainya dengan baik, kedepannya akan sangat membantu Anda pada saat mengembangkan sebuah aplikasi sungguhan.

Untuk membantu Anda memahami bagaimana menggunakan percabangan if di C#, buatlah sebuah aplikasi console dengan Visual Studio Code atau editor pilihan Anda bernama KonstruksiPercabanganIf.

Pernyataan If

Pernyataan if digunakan untuk mengevaluasi suatu kondisi. Konstruksi ini akan mengecek apakah suatu kondisi bernilai benar (true) atau salah (false). Jika kondisinya bernilai 

true
, maka kode program di dalam blok pernyataan if tersebut akan dieksekusi. Sebaliknya jika bernilai 
false
, maka kode program di dalam blok tersebut akan diabaikan.

Tulis kode program di bawah ini

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace percabanganIf
{
    class Program
    {
        static void Main(string[] args)
        {
            int angka = 30;
            if (angka == 30// Kondisi yang harus dipenuhi
            {
                // Kode program di dalam blok pernyataan if
                Console.WriteLine("Angka = 30");
                Console.ReadLine();
            }
        }
    }
}

Untuk mengevaluasi suatu kondisi apakah bernilai 

true
 atau 
false
, kita perlu membandingkan dua buah nilai atau variabel. C# menyediakan beberapa operator untuk mengimplementasikan hal tersebut.

Operator Kesataraan

C# menyediakan paling tidak dua buah operator untuk mengevaluasi apakah kedua nilai yang dibandingkan setara (mempunyai nilai dan tipe data yang identik) atau tidak.

Operator pertama menggunakan notasi dua tanda sama dengan (==). Operator ini akan menghasilkan nilai 

true
 apabila kedua nilai yang dibandingkan setara, sebaliknya menghasilkan nilai 
false
 jika nilainya tidak setara.

Operator kedua menggunakan kombinasi notasi tanda seru dan tanda sama dengan (!=). Berkebalikan dengan operator ==, operator ini justru menghasilkan nilai 

true
 apabila kedua nilai yang dibandingkan tidak setara dan sebaliknya menghasilkan nilai 
false
 apabila nilai keduanya setara.

Tambahkan kode program di bawah ini ke dalam program KonstruksiPercabanganIf yang telah Anda buat sebelumnya, lalu ubah nilai variabel 

umur
 dan 
pass
 untuk bereksperimen.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace percabanganIf
{
    class Program
    {
        static void Main(string[] args)
        {
            int umur = 20;
            if (umur == 17// Jika nilai variabel "umur" adalah dari tipe int dan setara dengan 17
            {
                Console.WriteLine("Anda berhak mendapatkan SIM!"); 
            }
            string pass = "password";
            if (pass != "password"// Jika tidak setara
            {
                Console.WriteLine("Akses Anda ditolak!");
            }
            Console.ReadLine();
        }
    }
}

Operator Relasional

Selain perlu membandingkan apakah kedua nilai setara atau tidak, seringkali kita juga perlu membandingkan apakah suatu nilai lebih besar atau lebih kecil dari nilai yang lain.

Tambahkan kode program berikut ini ke dalam program Anda.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace percabanganIf
{
    class Program
    {
        static void Main(string[] args)
        {
            int nilai;
            nilai = 80;
            if (nilai < 50)
            {
                Console.WriteLine("Nilai Anda kurang dari 50");
            }
            if (nilai > 50)
            {
                Console.WriteLine("Nilai Anda lebih dari 50");
            }
            if (nilai <= 50)
            {
                Console.WriteLine("Nilai Anda kurang dari atau sama dengan 50");
            }
            if (nilai >= 50)
            {
                Console.WriteLine("Nilai Anda lebih dari atau sama dengan 50");
            }
            Console.ReadLine();
        }
    }
}

Latihan: Ubah nilai dari variabel 

nilai
 dengan nilai yang lain, lalu perhatikan blok pernyataan if yang mana yang akan dieksekusi.

Pernyataan if-else Dan Operator Ternary (?:)

Adakalanya ketika hasil evaluasi terhadap kondisi di dalam pernyataan if bernilai 

false
, selain mengabaikan eksekusi baris kode program di dalam blok pernyataan if tersebut, kita juga ingin memberikan sebuah alternatif untuk dieksekusi.

Misalnya, Anda membuat program untuk menentukan batas nilai kelulusan. Jika nilainya lebih atau sama dengan 60, maka dinyatakan lulus. Alternatifnya apabila nilainya diluar kondisi tersebut, maka dinyatakan tidak lulus.

Untuk mengimplementasikan skenario seperti ini, C# menyediakan pernyataan if-else seperti yang ditunjukkan oleh kode program di bawah ini.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace percabanganIf
{
    class Program
    {
        static void Main(string[] args)
        {
            int nilai;
            nilai = 80;
            if (nilai < 50)
            {
                Console.WriteLine("Nilai Anda kurang dari 50");
            }
            if (nilai > 50)
            {
                Console.WriteLine("Nilai Anda lebih dari 50");
            }
            if (nilai <= 50)
            {
                Console.WriteLine("Nilai Anda kurang dari atau sama dengan 50");
            }
            if (nilai >= 50)
            {
                Console.WriteLine("Nilai Anda lebih dari atau sama dengan 50");
            }
            Console.ReadLine();
        }
    }
}

Apabila di dalam pernyataan if hanya terdapat satu baris kode program saja, maka kita bisa menghilangkan tanda kurung kurawal {} pada blok pernyataan if tersebut. Hal ini juga berlaku untuk blok pernyataan else. Dengan demikian, kode program di atas dapat kita tulis seperti berikut ini.

if (nilai >= 60)
Console.WriteLine("Anda dinyatakan lulus!");
else
Console.WriteLine("Anda tidak lulus!");

Lebih jauh lagi, untuk menyederhanakan pernyataan if-else seperti di atas, C# menyediakan konstruksi yang lebih simpel bernama operator ternary seperti yang ditunjukkan oleh baris kode program berikut ini.

Console.WriteLine(nilai >= 60 ? "Anda dinyatakan lulus!" : "Anda tidak lulus");

Ekspresi di sebelah kiri tanda ‘?’ merupakan kondisi yang akan dievaluasi. Jika kondisi ini bernilai 

true
, maka ekspresi di sebelah kiri tanda ‘:’ yang akan dieksekusi. Sebaliknya jika kondisinya bernilai 
false
, maka ekspresi di sebelah kanan tanda ‘:’ yang akan dieksekusi.

Pertanyaannya, apakah kita selalu bisa menggunakan operator ternary untuk setiap pernyataan if-else? Jawabannya, tidak. Operator ternary hanya bisa digunakan pada skenario, di mana hanya terdapat satu pernyataan/ekspresi saja pada masing-masing blok if dan else.

Kode program seperti di bawah ini tidak bisa Anda sederhanakan menggunakan operator ternary karena terdapat dua pernyataan/ekspresi di dalam blok pernyataan if.

if (kondisi)
{
pernyataan/ekspresi1;
pernyataan/ekspresi2;
}
else
{
pernyataan/ekspresi;
}

Menggunakan Operator Logika

Selain menggunakan operator kesetaraan dan operator relasional, kita juga bisa menggunakan operator logika untuk mengevaluasi kondisi pada pernyataan if. Operator logika biasanya kita gunakan untuk mengevaluasi lebih dari satu kondisi. C# menyediakan beberapa operator logika yang bisa kita gunakan, seperti AND, OR, XOR, dan NOT.

Operator AND

Di C#, notasi untuk operator AND adalah tanda ‘&’ dan ‘&&’.

Dengan menggunakan operator AND, konsekuensinya semua kondisi yang dievaluasi harus terpenuhi atau bernilai 

true
 agar kondisi pada pernyataan if bisa menghasilkan nilai 
true
.

Misalnya, ketika kita diminta untuk mengisi username dan password pada sebuah aplikasi, keduanya akan dievaluasi terlebih dahulu apakah merupakan data yang valid atau tidak. Lebih lagi, kedua-duanya harus merupakan data yang valid agar kondisinya terpenuhi.

Tabel di bawah ini menunjukkan hasil dari evaluasi operasi AND terhadap dua buah kondisi.

Kondisi 1Kondisi 2Hasil Operasi AND
truetruetrue
truefalsefalse
falsetruefalse
falsefalsefalse
Tabel Hasil Operasi AND

Untuk membantu Anda memahami operator AND (‘&’ dan ‘&&’), tambahkan kode program di bawah ini ke dalam program Anda.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace percabanganIf
{
    class Program
    {
        static void Main(string[] args)
        {
            string user = "madun";
            string pwd = "123456";
            // Operator & (Logika AND)
            if (user == "madun" & pwd == "123456")
            {
                Console.WriteLine("Anda berhasil login");
            }
            else
            {
                Console.WriteLine("Akses Anda ditolak!");
            }
            // Operator && (Logika bersyarat AND)
            if (user == "madun" && pwd == "123456")
            {
                Console.WriteLine("Anda berhasil login");
            }
            else
            {
                Console.WriteLine("Akses Anda ditolak!");
            }
            Console.ReadLine();
        }
    }
}

Latihan: Ganti nilai variabel 

user
 dengan nama masing-masing, lalu perhatikan baris kode program yang mana yang dieksekusi. Apakah baris kode program pada blok if atau blok else. Lakukan hal yang sama untuk variabel 
pwd
.

Perbedaan Antara Notasi ‘&’ Dengan ‘&&’

Ketika menjalankan kode program di atas, Anda akan menemukan bahwa blok pernyataan if-else pertama (menggunakan ‘&’) menghasilkan keluaran yang sama dengan blok pernyataan if-else kedua (menggunakan ‘&&’).

Lalu apa perbedaan antara menggunakan notasi ‘&’ dengan ‘&&’?

Meskipun menghasilkan keluaran yang sama, menggunakan notasi ‘&’ secara semantik (bagaimana suatu kode program dijalankan) cukup berbeda dengan menggunakan notasi ‘&&’.

Dengan menggunakan notasi ‘&’, kedua kondisi akan dievaluasi apakah bernilai 

true
 atau 
false
, tanpa kecuali. Jika kedua kondisi bernilai 
true
, maka hasil evaluasi secara kesuluruhan akan bernilai 
true
. Sedangkan jika satu saja kondisi bernilai 
false
, maka hasil evaluasi secara keseluruhan akan bernilai 
false
.

Sedangkan, dengan menggunakan notasi ‘&&’, pertama-tama kondisi pertama akan dievaluasi terlebih dahulu apakah bernilai 

true
 atau 
false
. Jika kondisi pertama bernilai 
false
, maka hasil evaluasi secara keseluruhan langsung bernilai 
false
 tanpa menghiraukan apa hasil dari kondisi kedua. Sebaliknya jika kondisi pertama bernilai 
true
, maka kondisi kedua baru akan dievaluasi. Apabila kondisi kedua juga bernilai 
true
, maka hasil evaluasi secara keseluruhan akan bernilai 
true
, sebaliknya akan bernilai 
false
 jika kondisi kedua bernilai 
false
.

Untuk membantu mempermudah pemahaman Anda, tambahkan kode program di bawah ini ke dalam program Anda.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace percabanganIf
{
    class Program
    {
        static void Main(string[] args)
        {
            bool a = false & KondisiKedua();
            Console.WriteLine("Nilai a: {0}"a);
            Console.WriteLine("========================");
            bool b = true & KondisiKedua();
            Console.WriteLine("Nilai b: {0}"b);
            Console.WriteLine("========================");
            bool c = false && KondisiKedua();
            Console.WriteLine("Nilai c: {0}"c);
            Console.WriteLine("========================");
            bool d = true && KondisiKedua();
            Console.WriteLine("Nilai d: {0}"d);

            Console.ReadLine();
            // Sampai baris ini
        }

        // Tambahkan juga method berikut ini setelah method Main() 
        static bool KondisiKedua()
        {
            Console.WriteLine("Kondisi ini juga dievaluasi");
            return true;
        }
        
    }
}

Pada kode program di atas, kita mendefinisikan sebuah method bernama 

KondisiKedua()
 yang mengembalikan nilai boolean 
true
 ketika dijalankan. Method sendiri baru akan kita pelajari di materi lainnya. Untuk saat ini, Anda tidak perlu terlalu mengkhawatirkan bagaimana sebuah method bekerja.

Dengan menggunakan notasi ‘&’, method 

KondisiKedua()
 ini selalu dipanggil/dijalankan tidak peduli apakah kondisi pertama bernilai 
true
 ataupun 
false
. Buktinya, teks “Kondisi ini juga dievaluasi” selalu muncul ketika menggunakan notasi ini.

Di lain pihak, Dengan menggunakan notasi ‘&&’, method 

KondisiKedua()
 hanya dipanggil/dijalankan ketika kondisi pertama bernilai 
false
 saja. Ketika kondisi pertama bernilai 
true
, maka method ini akan diabaikan. Buktinya, teks “Kondisi ini juga dievaluasi” hanya muncul ketika kondisi pertama bernilai 
false
 saja.

Operator OR

Berbeda dengan operator AND yang akan menghasilkan nilai 

true
 hanya apabila kedua kondisi bernilai 
true
, operator OR akan menghasilkan nilai 
true
, jika salah satu kondisinya bernilai 
true
. Di C#, operator OR menggunakan notasi ‘|’ dan ‘||’.

Tabel di bawah ini menunjukkan hasil dari evaluasi operasi OR terhadap dua buah kondisi.

Kondisi 1Kondisi 2Hasil Operasi OR
truetruetrue
truefalsetrue
falsetruetrue
falsefalsefalse
Tabel Hasil Operasi OR

Untuk membantu Anda memahami operator OR (‘|’ dan ‘||’), tambahkan kode program di bawah ini ke dalam program Anda.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace percabanganIf
{
    class Program
    {
        static void Main(string[] args)
        {
            int umur;
            umur = 3;
            int tinggi = 110;
            // Operator | (Logika OR)
            if (umur >= 3 | tinggi >= 100)
            {
                Console.WriteLine("Anda diperbolehkan bermain");
            }
            else
            {
                Console.WriteLine("Umur dan tinggi Anda belum mencukupi untuk bermain");
            }
            // Operator || (Logika bersyarat OR)
            if (umur >= 3 || tinggi >= 100)
            {
                Console.WriteLine("Anda diperbolehkan bermain");
            }
            else
            {
                Console.WriteLine("Umur dan tinggi Anda belum mencukupi untuk bermain");
            }
           
            Console.ReadLine();
        }
    }
}

Latihan: Ubah nilai variabel 

umur
, lalu perhatikan baris kode program yang mana yang dieksekusi. Apakah baris kode program pada blok if atau blok else. Lakukan hal yang sama untuk variabel 
tinggi
.

Menggunakan Notasi ‘|’ VS ‘||’

Jalankan kode program di atas. Anda akan menemukan bahwa blok pernyataan if-else pertama (menggunakan ‘|’) menghasilkan keluaran yang sama dengan blok pernyataan if-else kedua (menggunakan ‘||’).

Untuk mengetahui perbedaan menggunakan notasi ‘|’ dengan menggunakan notasi ‘||’, tambahkan kode program di bawah ini ke dalam program Anda.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace percabanganIf
{
    class Program
    {
        static void Main(string[] args)
        {
            bool a = false & KondisiKedua();
            Console.WriteLine("Nilai a: {0}"a);
            Console.WriteLine("========================");
            bool b = true & KondisiKedua();
            Console.WriteLine("Nilai b: {0}"b);
            Console.WriteLine("========================");
            bool c = false && KondisiKedua();
            Console.WriteLine("Nilai c: {0}"c);
            Console.WriteLine("========================");
            bool d = true && KondisiKedua();
            Console.WriteLine("Nilai d: {0}"d);
            Console.WriteLine("========================");
            bool f = false | KondisiKedua();
            Console.WriteLine("Nilai f: {0}"f);
            Console.WriteLine("========================");
            bool g = true | KondisiKedua();
            Console.WriteLine("Nilai g: {0}"g);
            Console.WriteLine("========================");
            bool h = false || KondisiKedua();
            Console.WriteLine("Nilai h: {0}"h);
            Console.WriteLine("========================");
            bool i = true || KondisiKedua();
            Console.WriteLine("Nilai i: {0}"i);

            Console.ReadLine();
            // Sampai baris ini
        }

        // Tambahkan juga method berikut ini setelah method Main() 
        static bool KondisiKedua()
        {
            Console.WriteLine("Kondisi ini juga dievaluasi");
            return true;
        }
        
    }
}

Jalankan kode program di atas, lalu perhatikan pada kondisi seperti apa method 

KondisiKedua()
 dipanggil/dijalankan?

Operator XOR

Operator XOR ini cukup spesial. Jika kedua kondisi bernilai sama, baik itu sama-sama bernilai 

true
 maupun 
false
, operator XOR justru menghasilkan nilai 
false
. Untuk menghasilkan nilai 
true
 ketika menggunakan operator XOR, kedua kondisi harus memiliki nilai yang berbeda. Di C#, operator XOR menggunakan notasi ‘^’.

Tambahkan kode program di bawah ini ke dalam program Anda.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace percabanganIf
{
    class Program
    {
        static void Main(string[] args)
        {
            bool a = false & KondisiKedua();
            Console.WriteLine("Nilai a: {0}"a);
            Console.WriteLine("========================");
            bool b = true & KondisiKedua();
            Console.WriteLine("Nilai b: {0}"b);
            Console.WriteLine("========================");
            bool c = false && KondisiKedua();
            Console.WriteLine("Nilai c: {0}"c);
            Console.WriteLine("========================");
            bool d = true && KondisiKedua();
            Console.WriteLine("Nilai d: {0}"d);
            bool f = false | KondisiKedua();
            Console.WriteLine("Nilai f: {0}"f);
            Console.WriteLine("========================");
            bool g = true | KondisiKedua();
            Console.WriteLine("Nilai g: {0}"g);
            Console.WriteLine("========================");
            bool h = false || KondisiKedua();
            Console.WriteLine("Nilai h: {0}"h);
            Console.WriteLine("========================");
            bool i = true || KondisiKedua();
            Console.WriteLine("Nilai i: {0}"i);

            Console.WriteLine("Logika true XOR true bernilai: {0}"true ^ true);
            Console.WriteLine("Logika true XOR false bernilai: {0}"true ^ false);
            Console.WriteLine("Logika false XOR true bernilai: {0}"false ^ true);
            Console.WriteLine("Logika false XOR false bernilai: {0}"false ^ false);

            Console.ReadLine();
            // Sampai baris ini
        }

        // Tambahkan juga method berikut ini setelah method Main() 
        static bool KondisiKedua()
        {
            Console.WriteLine("Kondisi ini juga dievaluasi");
            return true;
        }

    }
}

Jika Anda perhatikan keluaran yang dihasilkan oleh kode program di atas, terlihat bahwa operator XOR akan menghasilkan nilai 

true
jika dan hanya jika kedua kondisi memiliki nilai yang berbeda.

Tabel di bawah ini menunjukkan hasil dari evaluasi operasi XOR terhadap dua buah kondisi.

Kondisi 1Kondisi 2Hasil Operasi XOR
truetruefalse
truefalsetrue
falsetruetrue
falsefalsefalse
Tabel Hasil Operasi XOR

Operator NOT

Operator ini digunakan untuk membalik nilai boolean sebuah kondisi. Jika sebuah kondisi bernilai 

true
, menggunakan operator NOT akan membalik kondisi tersebut menjadi bernilai 
false
. Di C#, operator NOT menggunakan notasi ‘!’.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace percabanganIf
{
    class Program
    {
        static void Main(string[] args)
        {
            int umur;
            umur = 16;
            // Menggunakan operator relasional untuk menetapkan nilai variabel boolean
            bool dewasa = umur >= 17;
            if (!dewasa)
            {
                Console.WriteLine("Umur Anda belum mencukupi!");
            }
            Console.ReadLine();
        }

    }
}

Jalankan kode program di atas, lalu perhatikan keluaran yang Anda dapatkan. Ganti nilai variabel 

umur
 untuk bereksperimen.

Pernyataan If – Else if

Jika sebelumnya dengan menggunakan pernyataan if-else, kita bisa memberikan alternatif alur eksekusi untuk suatu kondisi tertentu, dengan menggunakan pernyataan if – else if, kita bisa memiliki beberapa alternatif kondisi yang bisa dievaluasi.

Misalnya, untuk memutuskan nilai seorang mahasiswa apakah akan diberi nilai A, B, C, D, E, atau F, seorang dosen perlu memiliki beberapa kondisi berupa rentang nilai angka.

Skenario di atas dapat disimulasikan oleh kode program seperti di bawah ini.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace percabanganIf
{
    class Program
    {
        static void Main(string[] args)
        {
            int nilai;
            nilai = 79;
            if (nilai >= 0 & nilai < 20)
            {
                Console.WriteLine("Nilai Anda: E");
            }
            else if (nilai >= 20 & nilai < 40)
            {
                Console.WriteLine("Nilai Anda: D");
            }
            else if (nilai >= 40 & nilai < 60)
            {
                Console.WriteLine("Nilai Anda: C");
            }
            else if (nilai >= 60 & nilai < 80)
            {
                Console.WriteLine("Nilai Anda: B");
            }
            else if (nilai >= 80 & nilai <= 100)
            {
                Console.WriteLine("Nilai Anda: A");
            }
            else
            {
                Console.WriteLine("Nilai yang diberikan berada di luar rentang yang dimungkinkan");
            }
            // output:
            // Nilai Anda: B
            Console.ReadLine();
        }

    }
}

Latihan: ganti nilai dari variabel 

nilai
 dan amati keluaran yang dihasilkan oleh kode program di atas.

Penutup

Pada materi kali ini, kita telah mempelajari bagaimana mengatur alur eksekusi sebuah program menggunakan percabangan if di C#.

Selain itu, kita juga mempelajari berbagai operator yang bisa digunakan untuk mengevaluasi kondisi pada sebuah pernyataan if, yaitu operator kesetaraan (== dan !=) dan operator logika (AND, OR, XOR, dan NOT).

Di materi selanjutnya Anda akan mempelajari satu lagi konstruksi percabangan di C#, yaitu pernyataan switch.

Jika Anda masih merasa kesulitan dalam memahami materi ini, jangan menyerah! Tulis kesulitan atau pertanyaan Anda di kolom komentar, saya akan membantu.

Selamat belajar!

Komentar

Postingan populer dari blog ini

Crack dan Assets Construct 2 by Madun

PROJEK C2 MATERI SHALAT

Membuat Portofolio dengan HTML dan CSS