-1

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?

4
  • Many cohesive things
    – Stefan
    Jan 9, 2019 at 20:56
  • What do you want? Me to duplicate the code example and divide the questions? And then refer maybe to the other questions from all other questions?
    – Stefan
    Jan 9, 2019 at 21:09
  • "Mutable" means changeable and is normally used to describe a variable, not a method. Methods can't be changed. I think the word you are looking for idempotent. When dealing with a database, whose whole purpose is to change and preserve changes to durable state, the concepts of immutability or idemopotence don't really make a lot of sense. Changing the data that is returned is the whole point.
    – John Wu
    Feb 12, 2019 at 22:57

2 Answers 2

0

State and behaviour are 2 different things, and the state of every class you've defined is unchanging after instantiation. Every class in your example is immutable.

Also note, immutable objects can work with mutable objects, and even return data that changes over time or is accessed by a dao. The whole term immutability is just referring to the state (think references to objects in class fields) not being changed.

I'm a Java developer, and in Java we're able to make class fields final (meaning they can be accessed, but the reference to the object cannot change). This allows forcing a class to be immutable.

public class MyImmutableClass {

    private final SomeMutableObject mutableObject;

    /**
     * Note: A mutable object doesn't break immutability
     */
    public MyImmutableClass(SomeMutableObject mutableObject) {
        this.mutableObject = mutableObject;
    }

    /**
     * We can even update the mutable object's state!
     * This still doesn't mean were updating this class' state.
     */
    public void updateState(State state) {
        mutableObject.setState(state);
    }

    /**
     * We can apply different logic based on time or randomness, doesn't matter.
     * We're always applying the same logic to the same objects.
     */
    public String[] getData() {
        int random = new Random().nextInt();
        LocalDate date = LocalDate.now();

        if (random < 10) {
            return null;
        }
        else if (date.getYear() > 2019) {
            throw new UnsupportedOperationException();
        }
        else {
            return mutableObject.getData();
        }
    }
}

The state of my object never changes, instead, the state of the random generator, the local date, and the mutable object change. But this class never updates it's state and thus is still immutable.

0

Question 1; can we say $paymentMethodManagerWithDbAwarePaymentMethod is immutable?

No. $paymentMethodManagerWithDbAwarePaymentMethod is a mutable variable, so clearly not immutable.

The object it contains is immutable by most definitions, but maybe not enough. If the dao is mutable, then it isn't deeply immutable which is necessary for certain concurrency guarantees. Also, if anything provided in enabledPaymentMethods is mutable you have similar problems.

Question 2: can we say $paymentMethodManagerWithTimeAwarePaymentMethod is immutable?

Likewise, the variable is clearly mutable and the same deep immutability concerns apply. The date fetching is behavior, not state. The date fetching makes that function impure, which has its own problems when it comes to concurrency but they're generally less severe than mutable state.

Question 3: Does immutability break encapsulation (as shown in this example...) sometimes?

Kind of. I'd look at it the other way though - leaky encapsulation more often breaks immutability by passing back mutable references from a shallow-immutable object.

This gets extra challenging when your language allows inheritance. Just because Foo is immutable doesn't mean its children necessarily are.

Question 4: Is there any OO language that deals with this issue?

Not that I am aware of in the general case for normally understood definitions of "OO language". The hard guarantee of immutability isn't as useful as practical immutability. As long as state doesn't change, you get tons of benefits when working concurrently and debugging systems and writing code.

Guaranteeing that statically is very hard and often requires limitations to the language that make it a pain to use and/or not very object oriented anymore.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.