Immutable objects
In object-oriented and functional programming, an immutable object (unchangeable[1] object) is an object whose state cannot be modified after it is created
Wikipedia (https://en.wikipedia.org/wiki/Immutable_object)
Code example in PHP (see question below...)
class ImmutablePaymentMethodManager
{
private $paymentMethods = [];
public function __construct(array $paymentMethods)
{
$this->paymentMethods = $paymentMethods;
}
public function enabledPaymentMethods() : iterable
{
$result = [];
foreach($this->paymentMethods as $paymentMethod) {
if($paymentMethod->enabled()) {
$result = $paymentMethod;
}
}
return $result;
}
}
class InMemoryPaymentMethod implements PaymentMethodInterface
{
private $name, $costs, $enabled;
public function __construct(string $name, float $costs, bool $enabled)
{
$this->name = $name;
$this->costs = $costs;
$this->enabled = $enabled;
}
public function name()
{
return $this->name;
}
public function costs() : float
{
return $this->costs;
}
public function enabled() : bool
{
return $this->enabled;
}
}
class DbAwarePaymentMethod implements PaymentMethodInterface
{
private $dao;
public function __construct(PaymentMethodDao $dao)
{
$this->dao = $dao;
}
public function name()
{
return 'My db aware payment method';
}
public function costs() : float
{
return $this->dao->getCosts($this->name);
}
public function enabled() : bool
{
return $this->dao->isEnabled($this->name);
}
}
class TimeAwarePaymentMethod implements PaymentMethodInterface
{
public function name()
{
return 'My time aware payment method';
}
public function costs() : float
{
return 33;
}
//only enabled at even 2,4,6,8,10,12,14,16,... hours
//is this considered a state change?
public function enabled() : bool
{
$hour = date('h');
return $hour % 2 === 0;
}
}
//Immutable (enabledPaymentMethods) we can expect the same results
$paymentMethodManager = new ImmutablePaymentMethodManager([
New InMemoryPaymentMethod()
]);
//Not immutable (enabledPaymentMethods) we cannot expect the same result
$paymentMethodManagerWithDbAwarePaymentMethod = new ImmutablePaymentMethodManager([
New InMemoryPaymentMethod(),
new DbAwarePaymentMethod(new PaymentMethodDao())
]);
//Not immutable (enabledPaymentMethods) we cannot expect the same result each time
$paymentMethodManagerWithTimeAwarePaymentMethod = new ImmutablePaymentMethodManager([
New InMemoryPaymentMethod(),
new TimeAwarePaymentMethod()
]);
Immutability
In the example above, encapsulation is a great way to hide the database details. But hiding the database logic in this DbAwarePaymentMethod now makes ImmutablePaymentMethodManager mutable, since it's result can very each time it is accessed.
I ask these questions, because I really like immutability, but I also like encapsulation like in the example above.
Assumption
We can say $paymentMethodManager is immutable. I will assume there is no debate about this.
Question 1; can we say $paymentMethodManagerWithDbAwarePaymentMethod is immutable?
Is accessing the database seen as a state change? Even the state of the object does not change, the state it communicates outwards does...
Question 2: can we say $paymentMethodManagerWithTimeAwarePaymentMethod is immutable?
Is the added behavior to paymentmethod.enabled(), seen as a state change?
Question 3: Does immutability break encapsulation (as shown in this example...) sometimes?
If all objects should be immutable, we must find a way of hiding the enabled-logic in another structure.
Question 4: Is there any OO language that deals with this issue?
Or are there any patterns (known to PHP) or other languages that make all payment method managers immutable? Those would include moving the behavior out of the payment method implementations and use InMemoryPaymentMethod for each payment method?