in reply to OO manner of accessing static variables in a subclass?

Hello HipDeep, and welcome to the Monastery!

I've also seen quite a few references to the concept that you should never access data structures directly, that you should always use a method. But I'm not sure that would apply directly in this case.

That is quite correct, and I don’t see why it wouldn’t apply in this case. Here is how I would refactor the modules:

package Monks::Data; use strict; use warnings; use parent 'Monks'; BEGIN { use Exporter(); our $VERSION = 20160808.00; our @ISA = qw(Exporter); } sub version { $Monks::Data::VERSION; } { my @fluffy; BEGIN { @fluffy = qw( Rabbits Minks Cats ); } sub is_fluffy { my $animal = shift; for (@fluffy) { return 'Yes' if $animal eq $_; } return 'No'; } } { my %textures; BEGIN { %textures = ( Rabbits => 'soft', Minks => 'supersoft', Cats => 'coarse', ); } sub get_texture { my $animal = shift; my $texture = 'unknown'; $texture = $textures{$animal} if exists $textures{$animal}; return $texture; } } 1;
package Monks; use strict; use warnings; use Monks::Data; BEGIN { use Exporter; our $VERSION = 0.01; our @ISA = qw(Exporter); } sub version { $Monks::VERSION } sub new { my $class = shift; my $self = { @_ }; bless $self, $class; return $self; }; sub check_fluffy { return Monks::Data::is_fluffy($_[1]); } sub find_fur_texture { return Monks::Data::get_texture($_[1]); } 1;

The value of this indirection is that it allows the implementation of package Monk::Data to be changed at a later date without impacting the code of its client(s). This is a general OO rule: keep the implementation separate from the public interface, and keep it private.

There are also some situations where I need to access the same type of data that is hard-coded into the main module. The common answer for that seems to be to copy that data into the object when it's created, but that seems inefficient in my case because I have some pretty large structures defined to handle a variety of cases where any individual object is only going to need a sliver of that data.

I don’t understand this. Can you elaborate? — preferably with example code.

Hope that helps,

Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Replies are listed 'Best First'.
Re^2: OO manner of accessing static variables in a subclass?
by HipDeep (Acolyte) on Aug 10, 2016 at 04:38 UTC

    Thanks, that does help. I had something similar worked up for the constants, but missed the idea of having the function that accesses the data in the subclass, instead of in the parent. So I can work with that. One question, what's the value of the methods in the parent like find_fur_texture, vs. simply calling the method from the child directly? Also, one of the things that concerned me when reading the warnings about inheritance was the idea of calling the methods by name (ala, Monks::Data::get_texture) as opposed to something more abstract. But maybe I'm missing something? And finally I am definitely keeping the goal of keeping the public interface and the internals separate. :)

    In regards to the last bit, one example would be a 2d hash with a bunch of compiled regular expressions that I'm using to parse the data that the module deals with. So something like this:

    if (not defined $Monks::Parsers{ $self->{thing} }) { $self->{parser} = 'Generic'; }

    And:

    foreach my $key (keys %{ $Monks::Parsers{ $self->{parser} } }) { dostuff; }

    Does that make sense?

      One question, what's the value of the methods in the parent like find_fur_texture, vs. simply calling the method from the child directly?

      Think in terms of a client/server architecture: a class (or an object instantiated from that class) is a server, and code which calls its (class or object) methods is a client. A server’s interface should publicly expose only the services it offers. In this case, find_fur_texture is a service (class method) offered by package Monk; the fact that it happens to be implemented inside that class by calling a class method in package Monk::Data is the sort of implementation detail that should remain private to the Monk class.

      Put another way: the client code’s access to the services it requires should be kept as simple as possible. Thus, it should use Monk; but it should not have to use Monk::Data.

      As Anonymous Monk and kcott have pointed out, there is no need (in the code shown) for package Monk::Data to use parent 'Monk'; — as kcott says, that looks like an OO design flaw. But I agree with kcott in seeing no problem with package Monk useing Monk::Data — that’s simply a part of Monk’s implementation.1

      Also, one of the things that concerned me when reading the warnings about inheritance was the idea of calling the methods by name (ala, Monks::Data::get_texture) as opposed to something more abstract.

      I don’t see a problem here. As stated above, clients of the Monk class shouldn’t need to know about the Monk::Data class at all; but clients of Monk::Data have to know the names of its methods, otherwise how can they call them?

      In regards to the last bit, one example would be a 2d hash with a bunch of compiled regular expressions that I'm using to parse the data that the module deals with.

      This doesn’t sound like a large data structure, only a collection of individual regexen. However, if you do have a data structure large enough to make the costs of copying prohibitive,2 that probably indicates that the data structure should itself be encapsulated into its own class. Then Monk::Data can hold and return an instance of that class3 without breaking encapsulation.

      1Yes, this creates a transitive dependency, but that’s really only an issue in compiled languages (see, e.g., the so-called Pimpl idiom in C++). In an interpreted language like Perl, the dependencies to be avoided are those which force client code to be rewritten when the server’s implementation code is changed.
      2Beware premature optimisation. Always test your code and profile carefully before expending effort on optimising something that may well be fine as it is.
      3In Perl, objects (class instances) are actually blessed references, so passing an object requires little overheard: only a pointer is copied under the hood.

      Hope that helps,

      Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        Very clear, thanks Athanasius!