Fendo has asked for the wisdom of the Perl Monks concerning the following question:

Hello, kind monks of Perl! Once again I come to ask for your wisdom, for I am short of good solutions on the problem I found.

To give some background and idea of structure, I have a fairly simple program which deals with files. The object-oriented ‘Path::Class’ package is used for basic operations on files, but because I need to store some additional info about the files, and do some custom operations, I have decided to make my own subclasses, to extend what ‘Path::Class’ can do. To illustrate the structure of the classes:

Now, I want to make my own ‘parent’ subroutine, which would return an ‘Own::Dir’ object of a file's (or a directory's) parent directory. Because it should work the same for ‘Own::File’ and ‘Own::Dir’, I want to define it in ‘Own’, so that those two can inherit it. The problem is that the subroutine needs to invoke Own::Dir->new in order to make the ‘Own::Dir’ object it's supposed to return. But when I write return $Own::Dir->new ($parent), Perl gives an error saying it ‘can't call method “new” on an undefined value’.

From what I can gather, this is because the ‘Own’ package can't really access the innards of the ‘Own::Dir’ package. I didn't want to mess around with cyclic inheritances, and the best thing I could think of was implementing the subroutine in ‘Own::Dir’ and making that a superclass to ‘Own::File’, but conceptually that wouldn't make much sense, and would be rather inelegant.

Is there a nice way of allowing ‘Own’ to invoke a method of ‘Own::Dir’? Should I maybe restructure the classes in a better way? For the record, I use the ordinary, built-in class (package) system. Thank you in advance!

Replies are listed 'Best First'.
Re: Calling subclass’ method from superclass
by haukex (Archbishop) on Jan 30, 2019 at 12:34 UTC
    I want to make my own ‘parent’ subroutine, which would return an ‘Own::Dir’ object of a file's (or a directory's) parent directory. Because it should work the same for ‘Own::File’ and ‘Own::Dir’, I want to define it in ‘Own’, so that those two can inherit it. The problem is that the subroutine needs to invoke Own::Dir->new in order to make the ‘Own::Dir’ object it's supposed to return.

    Since you are defining this subroutine to always return an object of type Own::Dir, I think it'd probably be fine to just call that constructor directly: return Own::Dir->new($parent) (no $ before Own).

    One thing to think about would be whether you want to make Own easily subclassable, such that this method on a subclass of Own would return a subclass of Own::Dir. In that case, one way to do that might be to abstract the creation of the object out into another method on Own, so that this method could be easily overridden by a subclass.

      Thank you; removing the sigil did the trick! However, I'm not sure if I understand your second paragraph, about subclassing, well. Are you saying I might want to move a part of the method into a different one, which could be utilised by similar methods to return objects of different classes (as opposed to only ‘Own::Dir’)?

        I think example code would go a long way here. If you think it's likely that someone might want to subclass Own and Own::Dir, then in the following, consider that in Own::parent, Own::Dir->new is hardcoded, and so, someone who wants to override Own::parent and call the superclass method, but wants to return their own subclass of Own::Dir, would have to find some workaround to do so. OTOH, if Own is some internal class, this case might be so unlikely that this may just be overthinking things.

        use warnings; use 5.012; package Own { sub parent { my $self = shift; say "called Own::parent()"; # Debug return Own::Dir->new('...'); } } package Own::File { use parent -norequire, 'Own'; use parent 'Path::Class::File'; } package Own::Dir { use parent -norequire, 'Own'; use parent 'Path::Class::Dir'; } say "Own::File->parent: ", Own::File->new('.')->parent; say "Own::Dir->parent: ", Own::Dir->new('.')->parent;
Re: Calling subclass’ method from superclass
by tobyink (Canon) on Jan 30, 2019 at 13:45 UTC

    Your issue seems to be that you're calling $Own::Dir->new instead of Own::Dir->new. $Own::Dir is the $Dir variable within the Own package. And it's undefined. Leave the dollar sign out.

    Also, for what it's worth, Own might be better written as a role instead of a class. There are various implementations of roles available, but I recommend starting with Role::Tiny.

    And another thing, Path::Tiny might be easier to subclass than Path::Class because it uses the same class for both files and directories. Apart from that, they have pretty similar APIs, so switching from one to the other is usually pretty easy.

      Thank you for your advice also! Looks like I misunderstood the dollar sign's role in the call. I will look into roles later, but in the meantime, I think I'm definitely going to try Path::Tiny out; thank you for the recommendation!