How would you prevent someone from calling new() directly, in this case skipping your argument checks?
Actually, this is possible. Your valid constructor methods just create a sufficiently secure random token and pass this to new. Here's a simple example where you can create objects with calls to the valid_constructor function, but not by directly calling new:
use 5.37.9;
use feature 'class';
no warnings 'experimental';
class No::New {
use Carp;
field $token :param;
my %valid_tokens;
ADJUST {
delete $valid_tokens{$token}
or croak "Directly calling ", __PACKAGE__,
"->new is forbidden";
}
sub valid_constructor {
my $token = rand;
$valid_tokens{$token} = 1;
__PACKAGE__->new(token => $token);
}
method text {
"Congratulations!";
}
}
say No::New->valid_constructor->text;
Such a technique, as already mentioned by tobyink, is entirely impossible for bless. Since all "traditional" Perl OO frameworks including Object::Pad are based on blessed references, only core OO can prevent objects like bless \q"Arbitrary junk", Your::Class from happening!