On 04/04/2024 01:54, Jordan LeDoux wrote:


On Tue, Apr 2, 2024 at 5:43 PM Saki Takamachi <s...@sakiot.com> wrote:


    If make a class final, users will not be able to add arbitrary
    methods, so I think making each method final. Although it is
    possible to completely separate behavior between method and opcode
    calculations, this is inconsistent and confusing to users and
    should be avoided.


    Right, if a class is not final but has only final methods
    (including constructor) and no protected properties then it allows
    people to extend it but not break any encapsulation or (I think)
    impose any more BC requirements on the maintainer (unless we care
    about conflicting with method names of child classes when
    updating). It lets people just do something a bit like adding
    'extension methods' in C#. I like it. People could write e.g.
    `$bcChildNumber->isPrime()` instead of
    `BcNumberUtils::isPrime($bcNumber)`


Yeah, I suspect the most common child class will be something like "Money" that additionally has a parameter denoting what currency the value is in, since that is by far the most common use case for arbitrary precision math in PHP. Making the calculation methods final should do fine in my opinion.

I don't think it will be possible to make a good Money class as a child of this. BcNum is a read-only class, so if the constructor of BcNum is final then the child class won't be able to take the currency as a constructor param, and won't be able to protect the invariant that currency must be initialized. If the constructor of BcNum were made non-final then BcNum wouldn't be able to protect the invariant of the numeric value always being initialized once the constructor has returned. And it should be straightforward to make a good money class as a composition of BcNum and a currency enum or string.

There's probably not any good use case for a subclass to add properties, unless perhaps the subclass developers are willing do do away with some of the checks that would normally be done on a value object by the PHP runtime to keep it valid (maybe replacing them with static analysis checks). But there are lots of reasonable use cases for subclasses to add methods, even if they're effectively syntax sugar for static methods with a BcNum typed param.

Having just written that I've now checked the behavior of DateTime - see https://3v4l.org/5DQZg . The constructor of that isn't final, so a child class can replace it with an empty constructor. But if it does that and leaves the value uninitialized then it blows up on a call to `format`. That's probably not something we should emulate. DateTimeImmutable behaves the same way.

Reply via email to