Aturan untuk bekerja dengan array dinamis dan kelas koleksi khusus
Berikut adalah aturan yang saya patuhi ketika bekerja dengan array dinamis. Sebenarnya, ini adalah panduan untuk merancang array, tetapi saya tidak ingin memasukkannya ke dalam panduan untuk mendesain objek, karena tidak setiap bahasa berorientasi objek memiliki array dinamis. Contoh-contoh ditulis dalam PHP karena mirip dengan Java (yang mungkin sudah Anda kenal), tetapi dengan array dinamis alih-alih kelas koleksi dan antarmuka bawaan.
Menggunakan array sebagai daftar
Semua item harus dari jenis yang sama.
Jika Anda menggunakan array sebagai daftar (kumpulan nilai dalam urutan tertentu), maka semua nilai harus dari jenis yang sama:
$goodList = [ 'a', 'b' ]; $badList = [ 'a', 1 ];
Gaya penjelasan jenis daftar umum adalah:
@var array<TypeOfElment>
. Pastikan Anda tidak menambahkan tipe indeks, itu harus selalu
int
.
Kita perlu mengabaikan indeks setiap elemen
PHP akan secara otomatis membuat indeks baru untuk setiap item daftar (0, 1, 2, dll.). Namun, Anda tidak boleh mengandalkan indeks ini, atau menggunakannya secara langsung. Klien hanya dapat mengandalkan
countable
iterable
dan
countable
.
Jadi, Anda dapat dengan bebas menggunakan
foreach
dan
count()
, tetapi jangan gunakan
for
menggilir item-item daftar:
Dalam PHP,
for
loop mungkin tidak berfungsi sama sekali jika tidak ada indeks dalam daftar, atau jika ada lebih banyak indeks daripada jumlah elemen.
Gunakan filter alih-alih menghapus item
Anda mungkin ingin menghapus item berdasarkan indeks (
unset()
), tetapi alih-alih menghapusnya, lebih baik membuat daftar baru tanpa elemen yang tidak diinginkan menggunakan
array_filter()
.
Sekali lagi, seseorang tidak harus bergantung pada indeks elemen. Jadi ketika menggunakan
array_filter()
jangan gunakan
parameter flag untuk menyaring elemen berdasarkan indeks, atau bahkan oleh elemen dan indeks.
Menggunakan Array sebagai Array Asosiatif
Jika kunci relevan dan bukan indeks (0, 1, 2, dll.), Maka gunakan array asosiatif secara bebas (koleksi dari mana Anda dapat mengekstraksi nilai dengan kunci unik mereka).
Semua kunci harus dari jenis yang sama.
Aturan pertama menggunakan array asosiatif: semua kunci harus dari tipe yang sama (paling sering adalah
string
).
$goodMap = [ 'foo' => 'bar', 'bar' => 'baz' ];
Semua nilai harus dari tipe yang sama.
Hal yang sama berlaku untuk nilai: mereka harus dari tipe yang sama.
$goodMap = [ 'foo' => 'bar', 'bar' => 'baz' ];
Gaya umum untuk
@var array<TypeOfKy, TypeOfValue>
keterangan jenis adalah:
@var array<TypeOfKy, TypeOfValue>
.
Array asosiatif harus tetap pribadi
Daftar, karena kesederhanaan karakteristiknya, dapat dengan aman dipindahkan dari satu objek ke objek lainnya. Setiap klien dapat menggilir elemen atau menghitungnya, bahkan jika daftar itu kosong. Peta lebih sulit untuk dikerjakan, karena klien dapat mengandalkan kunci yang tidak cocok dengan nilai apa pun. Ini berarti bahwa array asosiatif biasanya harus tetap pribadi sehubungan dengan objek yang mengelolanya. Alih-alih membiarkan klien mengakses pemetaan internal secara langsung, biarkan getter (dan mungkin setter) mengambil nilai. Lempar pengecualian jika tidak ada nilai untuk kunci yang diminta. Namun, jika Anda dapat menyimpan peta dan nilainya sepenuhnya pribadi, lakukanlah.
Gunakan objek sebagai array asosiatif dengan nilai dari beberapa tipe
Jika Anda ingin menggunakan array asosiatif, tetapi menyimpan nilai dari berbagai jenis di dalamnya, maka gunakan objek. Tentukan kelas, tambahkan properti tipe publik, atau tambahkan konstruktor dan getter. Objek tersebut termasuk objek konfigurasi atau perintah:
final class SillyRegisterUserCommand { public string $username; public string $plainTextPassword; public bool $wantsToReceiveSpam; public int $answerToIAmNotARobotQuestion; }
Pengecualian terhadap Aturan
Perpustakaan dan kerangka kerja kadang-kadang membutuhkan penggunaan array yang lebih dinamis. Maka tidak mungkin (dan tidak diinginkan) untuk mengikuti aturan sebelumnya. Contohnya termasuk
array data yang disimpan dalam tabel database, dan
konfigurasi formulir di Symfony.
Kelas Koleksi Kustom
Kelas koleksi kustom dapat menjadi alat yang hebat untuk bekerja dengan
Iterator
,
ArrayAccess
dan entitas lainnya, tetapi saya menemukan kode ini sering membingungkan. Siapa pun yang melihat kode untuk pertama kalinya harus berkonsultasi dengan manual PHP, bahkan jika ia adalah pengembang yang berpengalaman. Selain itu, Anda harus menulis lebih banyak kode untuk dipelihara (tes, debug, dll.). Jadi dalam kebanyakan kasus, array sederhana dengan anotasi jenis yang tepat sudah cukup.
Apa yang menunjukkan bahwa Anda perlu membungkus array dalam objek koleksi khusus?
- Duplikasi logika yang terkait dengan array.
- Klien harus bekerja dengan terlalu banyak detail tentang isi array.
Gunakan kelas koleksi khusus untuk mencegah logika duplikat.
Jika beberapa klien yang bekerja dengan array yang sama melakukan tugas yang sama (misalnya, memfilter, membandingkan, mengurangi, menghitung), maka duplikat dapat dihapus menggunakan kelas koleksi kustom. Mentransfer logika duplikat ke metode kelas koleksi memungkinkan setiap klien untuk melakukan tugas yang sama hanya dengan memanggil metode pengumpulan:
$names = [];
Keuntungan mengubah koleksi menggunakan metode adalah bahwa transformasi ini dinamai. Anda dapat menambahkan nama pendek dan informatif untuk memanggil
array_filter()
, yang jika tidak akan cukup sulit ditemukan.
Lepaskan ikatan pelanggan dengan kelas koleksi khusus
Jika klien siklus melalui array, mengambil bagian dari data dari elemen yang dipilih dan melakukan sesuatu dengan mereka, klien ini menjadi lekat dengan semua jenis yang sesuai: array, elemen, nilai yang diambil, metode pemilih, dll. bahwa karena ikatan yang begitu dalam, akan jauh lebih sulit bagi Anda untuk mengubah apa pun yang terkait dengan jenis ini tanpa melanggar klien. Dalam hal ini, Anda juga dapat membungkus array dalam kelas koleksi khusus dan memberikan jawaban yang benar, melakukan perhitungan yang diperlukan di dalam dan melonggarkan ikatan klien ke koleksi.
$lines = []; $sum = 0; foreach ($lines as $line) { if ($line->isComment()) { continue; } $sum += $line->quantity(); }
Beberapa aturan untuk kelas koleksi khusus
Buat mereka tidak berubah
Saat melakukan transformasi seperti itu, referensi yang ada ke instance koleksi tidak boleh terpengaruh. Oleh karena itu, metode apa pun yang melakukan konversi ini harus mengembalikan instance kelas baru, seperti yang kita lihat dalam contoh sebelumnya:
final class Names { private array $names; public function __construct(array $names) { Assert::that()->allIsString($names); $this->names = $names; } public function shortNames(): self { return new self( ); } }
Tentu saja, jika Anda mengonversi array internal, maka Anda dapat mengonversi ke tipe koleksi lain atau array sederhana. Seperti biasa, pastikan jenis yang benar dikembalikan.
Hanya berikan perilaku yang benar-benar dibutuhkan pelanggan
Alih-alih memperluas kelas dari perpustakaan dengan koleksi universal, atau menerapkan filter atau peta universal, serta mengurangi untuk setiap kelas koleksi kustom, hanya menerapkan apa yang benar-benar Anda butuhkan. Jika pada suatu saat Anda berhenti menggunakan metode ini, maka hapus saja.
Gunakan IteratorAggregate dan ArrayIterator untuk beralih
Jika Anda bekerja dengan PHP, maka alih-alih menerapkan semua metode antarmuka
Iterator
(menyimpan pointer internal, dll.),
ArrayIterator
hanya antarmuka
IteratorAggregate
dan biarkan ia mengembalikan instance
ArrayIterator
berdasarkan array internal:
final class Names implements IteratorAggregate { private array $names; public function __construct(array $names) { Assert::that()->allIsString($names); $this->names = $names; } public function getIterator(): Iterator { return new ArrayIterator($this->names); } } $names = new Names([]); foreach ($names as $name) {
Kompromi
Karena Anda menulis lebih banyak kode untuk kelas koleksi khusus, akan lebih mudah bagi klien untuk bekerja dengan koleksi ini (dan bukan hanya dengan satu larik). Jika kode klien menjadi lebih jelas, jika koleksi memberikan perilaku yang bermanfaat, maka ini membenarkan upaya ekstra untuk mempertahankan kelas koleksi kustom. Tetapi karena bekerja dengan array dinamis sangat mudah (terutama karena Anda tidak perlu menentukan jenis yang digunakan), saya jarang menggunakan kelas koleksi saya. Namun, beberapa pengembang aktif menggunakannya, jadi saya pasti akan terus mencari kemungkinan penggunaan case.