Koleksi adalah bagian yang paling sering bocor antar bagian program. Satu List<int> yang diteruskan mentah-mentah dari class domain ke lapisan UI membuka pintu bagi bagian lain untuk mengubah data tanpa melewati aturan bisnis. Bab penutup section ini merangkum kebiasaan desain yang menjaga koleksi tetap terkendali, dengan memanfaatkan kontrak interface dari bab Collection Interfaces dan tampilan baca-saja dari AsReadOnly().
Empat Prinsip Utama #
- Simpan mutable di dalam, tampilkan versi baca-saja ke luar: field internal boleh
List<T>, tetapi property publik sebaiknyaIReadOnlyList<T>atauIReadOnlyCollection<T>. - Terima parameter paling longgar: method yang hanya membaca cukup menerima
IEnumerable<T>, sehingga array,List<T>, maupun hasil LINQ bisa masuk tanpa konversi. - Arahkan perubahan lewat method: penambahan item dilakukan lewat method domain seperti
TambahNilai, bukan lewat koleksi mentah, agar aturan validasi tidak bisa dilewati. - Salin sebelum menyimpan: bila menerima koleksi dari penelepon, pertimbangkan
ToList()agar perubahan penelepon di kemudian hari tidak merembet ke data internal.
Contoh: NilaiSiswa yang Terkendali #
Class berikut menyimpan nilai di List<int> internal dan hanya membuka bacaan:
public class NilaiSiswa
{
private readonly List<int> _nilai = new List<int>();
public string Nama { get; }
public NilaiSiswa(string nama)
{
Nama = nama;
}
public void TambahNilai(int nilai)
{
if (nilai < 0 || nilai > 100)
{
throw new ArgumentOutOfRangeException(nameof(nilai), "Nilai harus 0-100.");
}
_nilai.Add(nilai);
}
public double RataRata => _nilai.Count == 0 ? 0 : _nilai.Average();
public IReadOnlyList<int> Nilai => _nilai.AsReadOnly();
}
NilaiSiswa siswa = new NilaiSiswa("Sinta");
siswa.TambahNilai(85);
siswa.TambahNilai(90);
siswa.TambahNilai(78);
Console.WriteLine($"Nilai {siswa.Nama}: {string.Join(", ", siswa.Nilai)}");
Console.WriteLine($"Rata-rata: {siswa.RataRata:0.0}");
Console.WriteLine($"Nilai kedua: {siswa.Nilai[1]}");Output yang Dihasilkan:
Nilai Sinta: 85, 90, 78
Rata-rata: 84.3
Nilai kedua: 90Mari kita bedah bagian pentingnya:
private readonly List<int> _nilai: satu-satunya penyimpanan asli; modifierprivatedanreadonlymenutup jalan bagi class lain untuk mengganti referensinya.TambahNilaimemvalidasi 0-100: satu-satunya pintu masuk data;ArgumentOutOfRangeExceptiondilempar sebelum item masuk, sehingga isi list dijamin sah.Nilai => _nilai.AsReadOnly(): property mengembalikan bungkusReadOnlyCollection<T>; penelepon bisa membaca dan mengindeks, tetapi tidak punyaAddmaupunRemove.RataRatadihitung saat dibaca: perhitungan menghindari state kedua yang bisa berbeda dari isi list; rumus LINQAverage()bekerja di atas koleksi internal.
Dengan empat prinsip di atas, koleksi yang kita rancang aman dikonsumsi bagian program mana pun. Materi section berikutnya, LINQ, membangun langsung di atas kontrak IEnumerable<T> yang menjadi fondasi semua bab di section ini.