Property dan accessor

Macro property, getter, dan setter untuk membuka akses pada variabel instans.

Daftar isi

Variabel instans bersifat tertutup bagi dunia luar, sehingga satu-satunya jalan membaca atau mengubahnya dari luar adalah melalui metode. Topik ini membahas macro getter, setter, dan property yang menghasilkan metode akses tersebut, varian bertipe dan varian predikatnya, serta bentuk manual yang setara. Setelah membaca topik ini, akses pada data objek dapat dibuka secara selektif tanpa menulis metode satu per satu.

Kebutuhan akan metode akses#

Class yang menyimpan data di variabel instans tetap tidak dapat menjangkau data itu dari luar objek. Ekspresi seperti sensor.ruang hanya bisa di-compile apabila class Sensor mendefinisikan metode bernama ruang, karena pemanggilan tersebut adalah pemanggilan metode seperti biasa. Menulis metode pembaca untuk setiap variabel memungkinkan, tetapi pekerjaan itu repetitif dan rawan keliru ketika nama atau tipenya berubah.

Crystal menjawab kebutuhan itu dengan tiga macro, yaitu getter, setter, dan property. Macro adalah kode yang dijalankan compiler pada saat compile untuk menghasilkan kode lain, dan ketiganya menghasilkan definisi metode akses (accessor) dari satu baris penulisan. Sintaks untuk menulis macro sendiri tidak dibahas di sini, sedangkan topik ini menggunakan ketiganya sebagaimana lazimnya, sebagai pembangkit metode.

Metode hasil macro tetap metode biasa dalam segala arti. Metode itu dapat dipanggil, didefinisikan ulang, dan didokumentasikan seperti metode lain, dan compiler tidak membedakannya dari metode tulisan tangan. Karena akses pada variabel instans selalu melalui metode, class juga menyimpan kebebasan untuk mengubah isi metode itu kelak tanpa mengubah pemanggilnya.

Macro getter, setter, dan property#

Macro getter ruang menghasilkan metode ruang yang mengembalikan nilai @ruang. Macro setter batas menghasilkan metode batas= yang menerima satu argumen dan mengisinya ke @batas. Macro property batas menggabungkan keduanya, sehingga variabelnya dapat dibaca dan diubah dari luar. Semuanya juga menerima bentuk bertipe, misalnya property batas : Int32, yang sekaligus mendeklarasikan tipe variabel instansnya. Untuk keadaan ber-Boolean, getter? aktif menghasilkan metode predikat aktif? yang namanya berakhiran tanda tanya, mengikuti konvensi metode yang menjawab ya atau tidak.

Perhatikan bahwa getter? hanya menghasilkan metode yang berakhiran tanda tanya. Pemanggilan Sensor.new.aktif tanpa tanda tanya ditolak compiler dengan pesan undefined method 'aktif' for Sensor yang disertai saran Did you mean 'aktif?'?, sehingga kekeliruan penamaannya langsung ketahuan dari pesan errornya. Konvensi ini membuat metode yang menjawab ya atau tidak mudah dibedakan dari metode yang mengembalikan data biasa.

Class Sensor berikut menggunakan ketiganya sekaligus. Ketiga macro ditulis di bagian atas badan class sebelum initialize, mengikuti kebiasaan umum agar daftar akses objek terbaca dalam sekali pandang. Bentuk bertipe seperti getter ruang : String menuliskan tipe variabel instansnya secara eksplisit pada daftar macro, sehingga tipenya terbaca tanpa perlu menelusuri isi initialize.

crystal sensor.cr
class Sensor
  getter ruang : String
  getter? aktif
  property batas : Int32

  def initialize(@ruang : String, @batas : Int32)
    @aktif = true
  end

  def matikan : Nil
    @aktif = false
  end
end

sensor = Sensor.new("Gudang A", 30)
puts sensor.ruang
puts sensor.batas
puts sensor.aktif?

sensor.batas = 35
puts sensor.batas

sensor.matikan
puts sensor.aktif?
bash
# jalankan program sensor.cr
crystal run sensor.cr
text Keluaran
Gudang A
30
true
35
false

Empat baris pertama keluaran menunjukkan metode hasil macro bekerja seperti metode tulisan tangan: sensor.ruang membaca @ruang, sensor.batas membaca @batas, dan sensor.aktif? membaca keadaan Boolean. Baris sensor.batas = 35 memanggil metode batas= hasil macro property, sehingga pengisian itu sah. Pemanggilan matikan mengubah @aktif menjadi false dari dalam class, dan pembacaan terakhir mengonfirmasi perubahannya melalui metode predikat.

Bentuk manual dan bukti pembangkitan#

Metode yang dihasilkan macro adalah metode biasa, tidak ada perbedaan bentuk maupun perilakunya dengan metode tulisan tangan. Bentuk manual yang setara dengan getter ruang : String ditulis seperti berikut.

crystal Contoh: bentuk manual yang setara dengan getter
# tidak-dijalankan
# Hasil yang sama dengan macro `getter ruang : String`
def ruang : String
  @ruang
end

Sifat “metode biasa” itu dapat dibuktikan dari sisi sebaliknya, yaitu apabila metode yang diperlukan tidak dihasilkan. Program berikut hanya memasang getter untuk ruang, lalu mencoba mengisinya dari luar. Penulisan sensor.ruang = "Ruang rapat" sebenarnya adalah pemanggilan metode ruang=, metode yang hanya dihasilkan oleh setter atau property.

crystal sensor_rusak.cr
class Sensor
  getter ruang : String

  def initialize(@ruang : String)
  end
end

sensor = Sensor.new("Lobi")
sensor.ruang = "Ruang rapat"
puts sensor.ruang
text Keluaran
Showing last frame. Use --error-trace for full trace.

In sensor_rusak.cr:9:8

 9 | sensor.ruang = "Ruang rapat"
            ^----
Error: undefined method 'ruang=' for Sensor

Pesan error menyebut ruang= dengan tanda sama dengan, persis nama metode yang tidak pernah dihasilkan. Kasus ini memperlihatkan bahwa macro bekerja pada saat compile: metodenya ada atau tidak ada sebelum program dijalankan, dan compiler menolak pemanggilan apa pun yang tidak tercantum. Perbaikannya sesuai kebutuhan, yaitu mengganti getter dengan property apabila pengisian dari luar memang diinginkan, atau memindahkan perubahan keadaan ke metode milik class seperti matikan pada contoh sebelumnya.

Perlu dicatat pula bahwa metode akses tidak harus berpasangan satu-satu dengan variabel instans. Metode apa pun yang mengembalikan nilai berlaku sebagai pembaca, termasuk metode yang menghitung hasilnya, misalnya metode luas yang mengalikan dua variabel instans. Macro baru diperlukan apabila nilai mentah sebuah variabel instans ingin dibuka apa adanya, sedangkan logika apa pun di baliknya ditulis sebagai metode biasa.

Ringkasan#

  • getter, setter, dan property adalah macro yang menghasilkan metode akses pada saat compile.
  • Varian bertipe seperti property batas : Int32 sekaligus mendeklarasikan tipe variabel instansnya.
  • getter? menghasilkan metode predikat berakhiran ? untuk keadaan ber-Boolean.
  • Bentuk manual def ruang yang mengembalikan @ruang setara dengan hasil macro, dan pengisian tanpa setter ditolak compiler.

Referensi#