php-ddd
php-ddd copied to clipboard
How to implement the Equatable interface / Equals or SameValueAs method in value objects
Here are some examples from the PHP world inside for instance from the @dddinphp book under "Value Equality":
- https://books.google.de/books?id=4nc5DwAAQBAJ&pg=PA50&lpg=PA50#v=onepage&q&f=false
Or by @spriebsch:
{
public function equals(Money $money)
{
if ($this->currency != $money->getCurrency()) {
return false;
}
return $this->amount = $money->getAmount();
}
}
- https://entwickler.de/php/ddd-mit-php-bausteine-fur-erfolgreiches-domain-driven-design/
Or by @lorenzomar :
public function sameValueAs(ValueObjectInterface $valueObject)
{
return $this->iso31661Alpha2Code->sameValueAs($valueObject->iso31661Alpha2Code()) &&
$this->iso31661Alpha3Code->sameValueAs($valueObject->iso31661Alpha3Code()) &&
$this->englishName->sameValueAs($valueObject->englishName()) &&
$this->phoneNumberPrefix->sameValueAs($valueObject->phoneNumberPrefix);
}
- https://hotexamples.com/de/examples/valueobject.geography.country/Iso31661Alpha2Code/-/php-iso31661alpha2code-class-examples.html
- https://github.com/lorenzomar/valueobject
Unfortunately implementing the interface in PHP can get tricky because of type-hinting.
Here is a SWIFT example by @twostraws:
struct User: Equatable {
let value: String
init?(string: String) {
guard string.trimmingCharacters(in: .whitespacesAndNewlines).count >= 3 else {
return nil
}
let illegalCharacters = ["@", "-", "&", "."]
guard illegalCharacters.contains(where: string.contains) == false else {
return nil
}
self.value = string
}
}
- https://www.hackingwithswift.com/articles/188/improving-your-swift-code-using-value-objects
Some thoughts from the .NET world by @jbogard:
Override the Equals method, to implement equality instead of identity, which is the default Additionally, Framework Design Guidelines has some additional requirements I must meet: Provide a reflexive, transitive, and symmetric implementation of Equals ... Implement IEquatable Override the equality operators Generic Implementation
What I wanted was a base class that would give me all of the Framework Design Guidelines requirements as well as the Domain Driven Design requirements, without any additional logic from concrete types. Here’s what I ended up with:
public abstract class ValueObject<T> : IEquatable<T>
where T : ValueObject<T>
{
public override bool Equals(object obj)
{
if (obj == null)
return false;
T other = obj as T;
return Equals(other);
}
- http://grabbagoft.blogspot.com/2007/06/generic-value-object-equality.html
IEquatable of T is designed to avoid boxing/unboxing of .NET value types during equality comparison. Its main purpose is to be used on structs. In classes, there's no benefit in implementing Equals(MyValueObject obj) over the standard Equals(object obj), hence I don't use this interface due to YAGNI.
- Unknown source
@EresDev states:
With improvement in my ideas, experience and knowledge, I no longer agree with having an interface for a Value Object. I don’t write interface for Value Objects anymore. The most solid reason for this is given by Kent Beck.
- https://eresdev.com/interface-for-value-object/
Some final thoughts by @vkhorikov:
- https://enterprisecraftsmanship.com/posts/value-object-better-implementation/
There's no need in implementing IEquatable
, this interface was introduced specifically to avoid boxing/unboxing of .NET value types (structs) when dealing with comparison. As long as you implement ValueObject as class, you can safely omit implementing IEquatable .
- https://enterprisecraftsmanship.com/posts/value-object-better-implementation/#comment-3498157602
Related discussion on Twitter:
- https://twitter.com/webdevilopers/status/1458365316048773122