On 2025-02-18 19:31, Rob Landers wrote:
On Tue, Feb 18, 2025, at 17:52, Juris Evertovskis wrote:
Hi,
A recent discussion brought up the need for multiple constructors for
the same class.
https://externals.io/message/126428
The need was mostly answered by suggestions to use factory methods.
However on an earlier discussion it was discussed how it's fine and
even useful for constructor methods to violate the LSP.
https://externals.io/message/121789
I've felt a similar need on some projects. To have a named factory
method that would have an overridable signature.
Maybe a new keyword like `factory`that would make the method only
callable statically? I'm thinking of something like
class Triangle extends Polygon
{
public factory method fromPoints(Point $p1, Point $p2, Point $p3):
static
{
// ...
}
public factory method wrap(static|string $triangle): static
{
return $triangle instanceof static ? $triangle :
static::fromWkt($triangle);
}
}
Sure, I'm not saying this API is currently impossible. I can do the
more narrow checks within the methods and throw from there. But
sometimes it's useful to precisely describe the acceptable arguments
in the method signature.
What do you think? Would such a feature be useless and/or harmful?
BR,
Juris
Hey Juris:
You may be interested in a super-draft RFC that I'll propose in the
next few days... (just fixing a couple of edge cases in the
implementation) it allows for nested classes and can support what you
are thinking. I will add this use case to the RFC.
Adapting your example:
class Triangle extends Polygon {
public final class fromPoints extends Triangle {
public function __construct(Point $p1, Point $p2, Point $p3) {}
}
public final class wrap extends Triangle {
public function __construct(Triangle $triangle) {}
}
}
$triange = new Triangle::fromPoints(...$points);
I don't know if this is what you are thinking of, but it basically
allows the expression you are suggesting. If we were to implement your
idea though, then nested classes would probably be impossible (and
vice-versa).
-- Rob
Hi Rob,
I'm a bit confused about the use of `final` in your example.
Would it allow defining Polygon::fromPoints such that it accepts
variable number of arguments and Triangle::fromPoints such that it only
accepts exactly 3 args?
Or having a hierarchy like `Square extends Rectangle extends Polygon`
while having a wrap with the following covariant behaviour?
- Polygon::wrap(Polygon $poly) -- accepts any Polygons
- Rectangle::wrap(Rectangle $poly) -- accepts Rectangles and Squares,
but not other Polygons
- Square::wrap(Square $poly) -- only accepts Square arguments
Juris