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.