Hi internals!

Separation of data and behaviour is both a fun and hard discussion,
especially considering:

* "It should be possible to add new features without touching old code"; and
* "Principle of Least Privilege" (never expose more than you have to)
(https://en.wikipedia.org/wiki/Principle_of_least_privilege).

There should (could) be a way to add new behaviour to old data without
touching the old data (class). Traits won't work in this use-case,
since they assume the same internal structure for all trait-using
classes. Imagine the `stringable` interface and a `toString` trait. A
__toString() method needs knowledge about the internal structure of a
class Foo. Yet if we want to keep adding behaviour to Foo, we'll end
up with either exposing too much of Foo, or expanding the class file
indefinitely. Please note that composition is not a proper solution,
since it requires exposure of Foo; composition leads to lack of proper
encapsulation, or representation exposure.

In Haskell it's possible to split instance implementation of
type-classes into separate files. In Rust you can have a struct with
private fields and put impl of behaviour in different files (but same
crate).

A similar feature in PHP could look like (using new keyword `expand`
but could be anything, or even `extend` in new context):

```
// File FooStringable.php
expand Foo implements stringable {
  public function __toString() {
    // Full access to Foo's all private fields here.
    // Assumes you can autoload Foo.
    // Assumes usage of $foo->__toString(); will be configured with
autoload to dynamically find the correct behaviour of Foo.
  }
}
```

If you'd use composition instead, you'd maybe have a formatter class
with a method `$formatter->toString(stringable $foo)`. This has the
problem I mentioned with exposing too much of $foo; it breaks
encapsulation. It has the benefit of being able to provide multiple
toString methods with different formats, but would have to assume
similar structure of the objects passed to it (defined with an
interface), which is not always possible or desirable.

The other way is inheritance, which doesn't scale over multiple
behaviours. `FooWithStringable extends Foo`? No.

Was I clear here? Do you understand the issues that this design
pattern is trying to solve? Its purpose is to solve "keep adding new
feature to old data" in a clean and proper way, while keeping
information encapsulation.

Enjoy the weekend!
Olle

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php

Reply via email to