Magento 2 - praktik yang baik untuk menggunakan / menghindari pengambil sihir?

21

Sihir getter pada Varien_Object(M1) dan DataObject(M2) adalah praktik umum, tetapi dengan Magento 2 rasanya salah menggunakannya.

Baik:

  • mudah dibaca / ditulis

Buruk

Pertanyaan

Dengan Magento 2 kami memiliki dua metode baru:

  • getDataByKey($key)
  • getDataByPath($path)

Apakah ada alasan bagus untuk tetap menggunakan getData($key)atau mendapatkan sihir?


Edit:

@Vinai terima kasih. Saya tidak menyebutkan @methodmetodenya, karena pendekatan saya sangat berbeda.

Ini hanya membantu IDE, tetapi tidak berdampak pada hal-hal lain.

Ada beberapa PR gabungan yang merupakan "optimasi mikro" seperti casting untuk (int)bukannya intval()atau mendapatkan ukuran array di luar loop (bahkan untuk array kecil).

Di sisi lain ada

  1. getter ajaib, yang memiliki "overhead" seperti yang dijelaskan Marius ....

    strtolower(trim(preg_replace('/([A-Z]|[0-9]+)/', "_$1", $name), '_'));
  2. getData($key) mehtods juga harus 2-3 pemeriksaan tambahan ...

    • if ('' === $key) {
    • if (strpos($key, '/')) {
    • if ($index !== null) {

Untuk kode sendiri, sepenuhnya setuju untuk memilih metode nyata, tetapi dalam kasus yang sama tidak mungkin ... misalnya Anda telah membuat acara khusus ...

$value = $observer->getVar_1();
$value = $observer->getData('var_1');
$value = $observer->getDataByKey('var_1');

Menggunakan ke-3 dengan yang /** @var some $value */terbaik bagi saya. (?)

sv3n
sumber
1
Anda bisa menambahkan metode di blok doc kelas, sehingga alat analisis kode tidak akan mengeluh tentang metode yang tidak ada. Juga saya pikir menggunakan angka dalam kunci itu sendiri merupakan praktik yang buruk, jadi seharusnya tidak terdaftar sebagai "Buruk" di sini.
Lily Bergonzat

Jawaban:

20

Pertanyaan di atas adalah tentang menggunakan metode sihir vs. getDataByKeyatau getDataByPath. Saya pikir ada opsi ketiga juga, dan itu menerapkan metode pengambil dan penyetel nyata.

Semua getData*metode memiliki kelemahan yang harus dicatat untuk inferensi tipe agar berfungsi.
Biasanya itu dilakukan dengan /* @var string $foo */anotasi di atas getData*panggilan.
Ini agak bau, karena jenis data harus dideklarasikan di kelas yang berisi data, bukan kelas yang memanggil getData*.
Alasan untuk itu adalah bahwa jika data berubah, kelas adalah yang paling mungkin diperbarui, tidak semua getData*situs panggilan.
Itu sebabnya saya pikir metode nyata meningkatkan rawatan dibandingkan dengan menggunakan getData*accessors.

Jadi saya pikir itu bermuara pada trade off antara pemeliharaan dan implementasi yang lebih cepat (lebih sedikit kode untuk ditulis).

Untungnya, saat ini IDE benar-benar pandai menciptakan implementasi pengambil dan penyetel bagi kami, sehingga argumen tersebut tidak benar-benar berlaku lagi.

Satu lagi argumen menentang pengambil getar dan setter ajaib yang hilang dari pertanyaan di atas adalah bahwa tidak mungkin membuat plugin untuk mereka.

Satu-satunya nilai lain yang saya pikir dapat saya tambahkan ke topik adalah mencoba mengumpulkan alasan untuk menggunakan atau tidak menggunakan @methodanotasi, jika menerapkan metode nyata tidak mungkin karena beberapa alasan.

Pro

  • Sebuah @methodpenjelasan adalah kode kurang sedikit untuk menulis dibandingkan dengan menerapkan getter nyata dan setter. Ini hampir tidak benar meskipun saat ini karena IDE pandai menghasilkan metode accessor, jadi itu bukan manfaat nyata lagi.

Cons

  • Sangat mudah untuk melakukan kesalahan.
    • Anotasi adalah komentar, mereka menjadi mudah usang ketika kode berevolusi tetapi anotasi tidak diperbarui. Metode nyata lebih kuat.
    • Dimungkinkan untuk menambahkan beberapa anotasi dengan tanda tangan tipe yang berbeda tanpa kesalahan juru bahasa - perilaku analisis kode statis tidak ditentukan, dan itu dapat menyebabkan bug halus yang sulit dilacak.
    • Jika ada @methodanotasi dan metode nyata dengan nama yang sama, tanda tangan jenis anotasi menimpa metode nyata selama analisis kode statis, yang merupakan kebalikan dari apa yang dilakukan penerjemah PHP. Ini lagi dapat dengan mudah menyebabkan bug halus.

Untuk alasan di atas, saya pribadi tidak menggunakan @methodanotasi jika saya dapat menghindarinya.
Untuk kode yang dimaksudkan untuk hidup lama saya menerapkan metode getter dan setter nyata. Keuntungan rawatan sebanding dengan upaya memicu IDE untuk menghasilkannya.

Untuk kode eksperimental yang lebih banyak selama spike, atau untuk detail implementasi sederhana dari sebuah modul, saya menggunakan getData*metode juga, karena saya malas.

Vinai
sumber
Ringkasan yang bagus. Vinai terima kasih. Itu menjawab lebih dari yang sebenarnya saya tanyakan.
sv3n
1

Semua getData*metode memiliki kelemahan yang harus dicatat untuk inferensi tipe agar berfungsi.

Biasanya itu dilakukan dengan /*@var string $foo */anotasi di atas getData*panggilan. Ini agak bau, karena tipe data harus dideklarasikan di kelas yang berisi data, bukan kelas yang memanggil getData *.

Alasan untuk itu adalah bahwa jika data berubah, kelas adalah yang paling mungkin diperbarui, tidak semua getData*situs panggilan. Itu sebabnya saya pikir metode nyata meningkatkan rawatan dibandingkan dengan menggunakan aksesor getData *.

Ya itu bau, tetapi bisakah (dan harus?) Dihindari. Saya pikir ini adalah kode yang sangat umum dan sering disarankan:

/** @var Foo $product */
$product = $model->getProduct()
if ($product->getId()) {
    $product->doSomething();
}

Masalahnya adalah Anda hanya menebak bahwa nilai kembali adalah tipe Foodengan getId()metode callable .

Untuk pemeliharaan, mengapa tidak menganggap jenis variabel dan menambahkan InvalidArgumentException?

$product = $model->getProduct()
if ($product instanceof Foo && $product->getId()) {
    $product->doSomething();
}

Ini juga memperbaiki analisis kode statis jika $model->getProduct()memiliki tipe pengembalian yang berbeda - seperti Foo|false. Dalam kasus pertama itu akan mengeluh tentang memanggil doSomething()yang mungkin false.

sv3n
sumber