sejak Objek nilai PHP Sekarang, karena itu penting, saya pikir saya akan menulis tentang mereka juga. Anda mungkin juga ingin membaca http://kacper.gunia.me/blog/ddd-building-blocks-in-php-value-object dan http://kacper.gunia.me/blog/validating-value-objects.
Oleh karena itu, objek nilai adalah objek yang dianggap setara berdasarkan nilainya, bukan identitasnya. Sebagai contoh, misalkan domain kita memesan makanan untuk dibawa pulang. Jadi dalam konsep ini kami memiliki konsep takeaway yaitu tempat dimana kita bisa memesan makanan. Karena begitulah kami menyebutnya di sini, padahal mereka hanya menyajikan makanan, karena, uh, alasannya.
Biasanya kita akan memodelkannya menggunakan entitas, bukan objek nilai. Hal ini memang tepat karena kesetaraan antara dua takeaways tidak ditentukan oleh nilai. Untuk saat ini, anggaplah satu-satunya properti yang mereka miliki hanyalah nama mereka. Jika kita memiliki dua takeaways dengan nama yang sama, keduanya bukan takeaway yang sama:
new Takeaway('Thai Tantic') != new Takeaway('Thai Tantic');
Band yang diubah namanya tetap bisa menjadi band yang sama:
$takeaway = new Takeaway('Wok around the clock');
$takeaway->changeName('Wok this way');
Jadi jika kita ingin memutuskan apakah keduanya setara, kita mempunyai suatu identitas, bukan sekedar nilai seperti nama.
Jika kita hanya memperhatikan nama-nama itu sendiri, maka dua nama dikatakan identik jika mempunyai arti yang sama. Hal ini jelas berlaku untuk string:
'Lord of the Fries' == 'Lord of the Fries';
'Man Fryday' != 'The Codfather';
Jika kita membuat objek untuk merangkum namanya, tidak perlu memiliki identitas, tetapi dapat dianggap sama jika nilainya sama:
new TakeawayName('Just Falafs')
== new TakeawayName('Just Falafs');
new TakeawayName('Abra Kebabra')
!= new TakeawayName('Jason Donnervan');
Oleh karena itu, jika kita ingin memodelkan nama eksternal sebagai sebuah objek, maka objek nilai adalah pilihan yang baik. Beberapa objek nilai (seperti TakeawayName) berisi satu nilai, namun mencoba fokus pada manfaat pembuatan objek bisa sedikit menyesatkan. Sebagai gantinya, mari kita lihat di mana kita ingin objek nilai memiliki beberapa properti.
Jadi, properti apa lagi yang mungkin dimiliki entitas Takeaway kita jika ia hanya menyimpannya sebagai nilai primitif? Konsep domain yang penting adalah wilayah di mana mereka akan dikirimkan. Sekarang mari kita asumsikan sebuah konsep sederhana yang ditentukan oleh lokasi dan jarak dari lokasi tersebut. Ini kemudian akan membentuk lingkaran di mana mereka akan menyampaikan. Jarak juga bisa dinyatakan dalam kilometer atau mil, jadi kita perlu tahu yang mana. Jadi kami memiliki yang berikut ini:
class Takeaway
{
//...
private $long;
private $lat;
private $distanceQuantity;
private $distanceUnits;
}
Jadi mari kita pikirkan tentang jarak. Ini terdiri dari dua nilai: satuan pengukuran dan kuantitas yang diukur. Besaran saja (misalnya 6) bukanlah jarak. Satuan jarak (misalnya kilometer) bukanlah jarak. 6 kilometer adalah jarak.
Keduanya harus sama agar jarak dianggap sama:
- 5 kilometer = 5 kilometer
- 5 kilometer! = 5 sentimeter
- 5 kilometer! = 8 kilometer
Oleh karena itu, menggunakan primitif sebagai dua bidang terpisah di entitas Takeaway tidak sepenuhnya menangkap hubungan antara nilai-nilai ini. Dimungkinkan untuk mengubah hanya satuan atau kuantitas saja, yang tampaknya bukan perilaku yang benar. Sebagai gantinya, kita dapat mengekstrak objek yang mewakili jarak, dan kemudian kita dapat mengubah seluruh objek jarak.
class Distance
{
private $quantity;
private $unit;
public function __construct($quantity, $unit)
{
$this->quantity = $quantity;
$this->unit = $unit;
}
public function equals(Distance $toCompare)
{
return $this->quantity == $toCompare->quantity
&& $this->unit == $toCompare->unit;
}
}
class Takeaway
{
//...
private $long;
private $lat;
/**
* @var Distance
*/
private $distance;
}
Sekali lagi, masing-masing properti sepertinya tidak cocok, yang sebenarnya kami minati adalah lokasinya yang ditentukan. Jadi mari kita jadikan ini sebuah objek juga, dan jadikan alasan mengapa kita tertarik pada nilai-nilai ini lebih eksplisit dalam model kita.
class Location
{
private $long;
private $lat;
public function __construct($long, $lat)
{
$this->long = $long;
$this->lat = $lat;
}
public function equals(Location $toCompare)
{
return $this->long == $toCompare->long
&& $this->lat == $toCompare->lat;
}
}
class Takeaway
{
//...
/**
* @var Location
*/
private $location;
/**
* @var Distance
*/
private $distance;
}
Oleh karena itu objek nilai tidak perlu membungkus satu nilai primitif. Selain itu, objek nilai tidak perlu hanya berisi primitif. Yang benar-benar kami minati adalah bidang di mana perusahaan bersedia memberikan layanan. Konsep jarak dan posisi tidak ditangkap dan dibuat eksplisit dalam kode. Jadi mari kita ekstrak objek yang mewakili area yang dicakup. Ia dapat terdiri dari objek nilai posisi dan jarak dan tidak memiliki nilai primitif itu sendiri.
class DeliveryArea
{
private $location;
private $radius;
public function __construct(Location $location, Distance $radius)
{
$this->location = $location;
$this->radius = $radius;
}
public function equals(DeliveryArea $toCompare)
{
return $this->location->equals($toCompare->location) && $this->radius->equals($toCompare->radius);
}
}
class Takeaway
{
//...
/**
* @var DeliveryArea
*/
private $areaCovered;
}
Kembali ke titik awal lagi, ini adalah objek nilai karena kesetaraan ditentukan oleh nilai, bukan status. Area yang dicakup tetap tidak teridentifikasi dan kita dapat menukarnya dengan objek lain dengan nilai yang sama tanpa masalah. Namun, tidak satu pun dari hal ini menjelaskan apa pun tentang perilaku. Menjadi objek nilai bukan berarti tidak memiliki perilaku melainkan hanya nilai.
Jadi jika kita mempunyai lokasi dan ingin mengetahui apakah perusahaan akan melakukan pengiriman ke lokasi tersebut, kita dapat menanyakan kepada objek perusahaan:
class Takeaway
{
//...
/**
* @var DeliveryArea
*/
private $areaCovered;
public function deliversTo(Location $location)
{
//determine if location falls within the area covered.
}
}
Cara ini dapat menghitung apakah posisinya berada di dalam area itu sendiri, namun lebih mudah jika menanyakan secara langsung objek Area apakah posisinya berada di dalam area tersebut:
class Takeaway
{
//...
/**
* @var DeliveryArea
*/
private $areaCovered;
public function deliversTo(Location $location)
{
return $this->areaCovered->includes($location);
}
}
Objek nilai tidak hanya mempunyai perilaku, tetapi juga menarik perilaku. Lebih baik membiarkan DeliveryArea memutuskan apakah akan menyertakan Lokasi daripada membiarkan Takeaway masuk ke DeliveryArea untuk mendapatkan nilainya dan mengambil keputusan. Ini merusak enkapsulasi dan menjadikannya metode di DeliveryArea yang dapat dipanggil dari tempat lain. Memasukkan logika yang terlibat dalam cek ke dalam takeaway akan mengikatnya ke objek yang salah.
Oleh karena itu, objek perusahaan kita sekarang hanya memiliki objek AreaCovered dan metode yang didelegasikan padanya untuk menentukan apakah ada lokasi tertentu di dalamnya. Satu hal yang perlu diperhatikan di sini adalah perusahaan tidak lagi mengetahui di mana areanya atau di mana lokasinya. Saat kami memulai, hal tersebut terkait dengan garis lintang, panjang, dan radius. Jika ada cara lain untuk mengidentifikasi lokasi dan area – seperti daftar kode pos yang tercakup – tidak ada yang perlu diubah di objek perusahaan untuk mendukung hal ini. Untuk melakukan ini, Anda dapat menggunakan implementasi berbeda dari objek Lokasi dan AreaCovered. Kita dapat mengekstrak antarmuka Lokasi dan AreaCovered dan memiliki implementasi yang berbeda tanpa mengubah entitas Takeaway.
Enkapsulasi data dan polimorfisme adalah manfaat dari orientasi objek. Jika kita hanya memiliki objek perusahaan dengan primitif dan logika untuk memutuskan apakah suatu lokasi diganti atau tidak, akan lebih sulit untuk mendukung wilayah dan jenis lokasi yang berbeda. Kami memperkenalkan lebih banyak objek, yang masing-masing objeknya sangat sederhana.
Selain itu, tingkat abstraksi objek perusahaan kini lebih tinggi. Kita dapat melihat bahwa ia memiliki area tertutup dan kita dapat mengetahui apakah ada lokasi tertentu yang termasuk di dalamnya. Untuk banyak tujuan, mungkin hanya ini yang perlu kita ketahui saat membaca kode. Kita tidak perlu khawatir tentang rincian bagaimana hal ini dicapai. Tanpa tingkat abstraksi ini, kita akan melihat perusahaan memiliki garis lintang, garis bujur, jumlah jarak, dan satuan jarak serta beberapa logika di sekitar nilai-nilai tersebut. Hal ini memberi tahu kita terlalu banyak detail dan tidak cukup tujuan, dan ini hanya akan menjadi lebih buruk jika kita ingin mendukung lebih banyak cara untuk merepresentasikan hal-hal ini.
Oleh karena itu, objek nilai berguna untuk merangkum data dan mengekspos perilaku terkait. Ya, tapi itu tidak spesifik untuk objek nilai Ya Berorientasi objek.
Bagaimana dengan kekekalan dan verifikasi? Ya, mereka bukannya tidak penting, tapi mereka bukanlah konten utama dari objek nilai. Objeknya tentang enkapsulasi dan polimorfisme. Objek nilai adalah bagian dari objek yang kesetaraannya ditentukan oleh nilai, bukan identitas.
Apapun itu, informasi lebih lanjut mengenai hal ini akan segera tersedia.