Make Children, not War
While teaching META6::bin how to read a config file, I was thinking about handling URIs by creating individual types per schema. That would require a factory what is not to my liking. I don’t use a dymanic language just to implement all that nice redundancy sugested in Design Patterns.
But then I reaslised that I use a dynamic language what means I can have a factory without implementing it. As long as I keep all subclasses in the same compunit then the parent class, that parent can use package introspection to collect all children and implement the factory method in its .new
method.
class URL is export { my $.subclasses; our $.schema; has Str $.host; has $.raw; multi method new(Str $url) { $.subclasses //= UNIT::.values.grep({$_ ~~ URL && .schema}); for $.subclasses.flat { return $_.new(:raw($url)) if $url.starts-with(.schema ~ '://'); } } method host { $!host //= $.raw.substr($.raw.index('://') + 3).substr(0, $.raw.index('/') + 1); } } class HTTP is URL is export { our $.schema = 'http'; } class HTTPS is URL is export { our $.schema = 'https'; } sub url(|c){ URL.new(|c) } say url('https://foo.com/bar.p6').host;
UNIT::.values
returns a list of type objects that we can check against in the constructor. The class variable $.schema
is used to pick the right child to return.
One could take that a step further and add a trait to register a child and a thunk with the base class to move the decision making what object to return to the children. That way a child could be moved to a different compunit as well.
-
April 10, 2017 at 22:582017.15 Kaboom! ⁽¹⁾ | Weekly changes in and around Perl 6