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

Dear monks,

I'd like to ask you for an advice regarding attribute accessors' naming.

I started to develop a project that is supposed to have quite a ramified hierarchy of classes, for example, the `SomeFramework` class, a bunch of classes like `SomeFramework::Logger` and, let's say, classes similar to `SomeFramework::SomeSubsystem::SomeComponent::SomeAPI` classes.

My goal is to design the most efficient communication between all these classes. I'll explain how I'm doing it now, so maybe you would like to share some opinions on how to make it better.

When I initialize the `SomeFramework` class, I have an object reference which I use from my application.

my $someframework = SomeFramework->new(parameter => 'value');

The `SomeFramework` class has some attributes, such as `logger`, `configuration`, etc, here are some examples of their definitions:

has 'logger' => ( is => 'ro', isa => 'SomeFramework::Logger', reader => 'get_logger', writer => '_set_logger', builder => '_build_logger', lazy => 1 ); sub _build_logger { my $self = shift; SomeFramework::Logger->new(someframework => $self); }

I'm passing the reference to the parent object to the child object, because I need the child to have access to the parent and its methods & accessors. So in the `SomeFramework::Logger` I have such attribute:

has 'someframework' => ( is => 'ro', isa => 'SomeFramework', reader => 'get_someframework', writer => '_set_someframework', required => 1 );

It lets me to have access to any object from within the `SomeFramework::Logger` class, usually it looks something like that:

my $configuration = $self-> get_someframework-> get_configuration-> get_blah_blah;

To extrapolate it, let's look into the `SomeFramework::SomeSubsystem::SomeComponent::SomeAPI` class. This class has its own "parent" attribute (let's call it `somecomponent`) which is supposed to have a reference to a `SomeFramework::SomeSubsystem::SomeComponent` object as the value. The `SomeFramework::SomeSubsystem::SomeComponent` class has the attribute for its own parent attribute (we can call it `somesubsystem`) which is supposed to contain a reference to a `SomeFramework::SomeSubsystem` object. And, finally, this class has the attribute for its own parent too (`someframework`), so it contains the reference to a `SomeFramework` object.

It all makes it possible to have something like that inside of the `SomeFramework::SomeSubsystem::SomeComponent::SomeAPI` class:

my $configuration = $self-> get_someframework-> get_somesubsystem-> get_somecomponent-> get_configuration-> get_blah_blah;

The first thing I'd like to know: is it a good practice? I hope, it is, but maybe you would advice me to go some more smooth way?

The second question is a bit more complicated (as for me), but I hope you'll help me with it. :) I like canonical names of accessors recommended by D.Conway in his "Perl Best Practices", but I'd like to do something like that:

my $configuration = $self->sc->ss->sf->conf->blah_blah;

Surely I can name all readers in this laconical manner:

has 'some_framework' => ( is => 'ro', isa => 'SomeFramework', reader => 'sf', writer => '_set_someframework', required => 1 );

But I don't like the idea of managing without the "standard" accessors names. :(

Also I can use `MooseX::Aliases`, it works fine for something like that:

has 'some_framework' => ( is => 'ro', isa => 'SomeFramework', reader => 'get_someframework', writer => '_set_someframework', required => 1, alias => 'sf' );

It looks fine, but there's an issue with attributes which names do NOT needed to be shortened. For example:

has 'api' => ( is => 'ro', isa => 'SomeFramework::API', reader => '_get_api', writer => '_set_api', required => 1, alias => 'api' );

In this case Moose throws an exception: `Conflicting init_args: (api, api) at constructor`. :( As I understand, MooseX::Aliases tries to create an attribute with the same value of the `init_args` parameter, so it fails. By the way, sometimes it happens, but sometimes it works fine, I haven't discovered when exactly it doesn't work.

Maybe I should have something like that:

has 'api' => ( is => 'ro', isa => 'SomeFramework::API', reader => '_get_api', writer => '_set_api', required => 1, handles => { api => 'return_self' # ^^^ It's supposed to have some method that # returns the reference to its own object } );
? But it doesn't seem to be the best option too, because it helps me only if the attribute contains a reference some object for which I can define the `return_self` method. If the attribute contains a reference to some "foreign" object or some other value (e.g., a hash), it won't be possible to call that method. :(

Ugh... Sorry for such a long rant! I hope, you have managed to read to here. :)

I'll be very happy to get to know what do you thing and what would you suggest to do. Feel free to share any your ideas on this topic, any fresh ideas will be very appreciated!

V.Melnik

Replies are listed 'Best First'.
Re: Names of attribute accessors (Moose)
by Athanasius (Archbishop) on Oct 23, 2015 at 13:33 UTC

    Hello v_melnik,

    I’m having trouble understanding your OO design. In part, I suspect it’s a problem of terminology. For example, you say:

    I'm passing the reference to the parent object to the child object...

    But in OO there’s no such thing as a “parent object” or a “child object.” There are parent and child classes, related by inheritance, but the inheritance relation does not extend to objects. For example, if you had a parent class Dog and a child class Labrador_Retriever, you could create two objects:

    my $rex = Dog->new(name => '$Rex'); my $fido = Labrador_Retriever->new(name => 'Fido');

    but it would be wrong to refer to $rex as the “parent” of $fido in this case. Actually, it’s also usually a bad design to allow a parent class like Dog to be instantiated at all. Dog should be an abstract base class.

    In Perl, there’s an additional source of confusion in the naming scheme of the module hierarchy. For example, if you use two classes like this:

    use Foo; use Foo::Bar;

    it seems natural to assume that Foo::Bar is a child class of Foo. But that needn’t be the case; Foo::Bar may be completely unrelated to Foo. The package name Foo::Bar simply tells Perl to search for the Bar package in a directory named Foo. To create an inheritance relationship (in Moose) you would normally use extends:

    package Foo::Bar; use Moose; extends 'Foo';

    Now to the real issue: inter-object communication. You write:

    I'm passing the reference to the parent object to the child object, because I need the child to have access to the parent and its methods & accessors.

    Why? If there’s a true inheritance relationship, then a child class object already has access to the methods in the parent class. In all other cases, communication between objects should be via the public interfaces of those objects. That — encapsulation — is a large part of what OO is all about! And if you have to subvert encapsulation to a large extent (as your use of attributes appears to be trying to do), that would tend to indicate a basic flaw in the OO design.

    But I apologise if I’ve misunderstood the design issue behind your question. If so, will you please explain why standard, public-interface-based communication between objects is inadequate to your requirements? Also, exactly what problem is the attribute-based design you’ve outlined intended to solve? Try to provide a short, self-contained example showing communication between two or three objects.

    Hope that helps,

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

      Thank you very much for your comment!

      I'll try to explain why exactly I need to have access to the object that has initialized the current object (that's what I mean when I call it "the parent object"). For example, let's imagine we have some class for the jobs-queue runner (let's call it SomeFramework::JobsQueue::Executor) and some class for jobs. Is it bad to do something like this:

      package SomeFramework::JobsQueue::Executor; use Moose; use MooseX::Params::Validate; has queue { isa => 'SomeFramework::JobsQueue', required => 1, reader => 'get_queue', writer => '_set_queue' } # This attribute is being set by the framework when the framework # creates the SomeFramework::JobsQueue::Runner-based object sub execute { my($self, $job, $options) = validated_hash( \@_, job => { isa => 'SomeFramework::JobsQueue::Job' }, options => { isa => 'HashRef' } ); my $queue = $self->get_queue; $queue->mark_as_running($job->get_id); $job->execute(options => $options); $queue->mark_as_completed($job->get_id); }

      ? So, the queue-runner object is aware about the queue object it "belongs" to, so it can call some methods of the queue object.

      Or let's look at much more simple example:

      package SomeFramework::SomeSubsystem; use Moose; has 'some_framework' => { isa => 'SomeFramework', required => 1, reader => 'get_some_framework', writer => '_set_some_framework' } sub some_method { my $self = shift; $self->get_some_framework->get_logger->log_trace("Hello, world!"); }

      So, our object knows how to call the framework's object that has initialized that object, so it can call some methods of the framework's object and even some methods of other objects initialized and stored by the framework's object.

      If it's really bad, would you be so kind as to help me to understand why? Thank you!

      V.Melnik

        Ok, now I’m a little less confused. :-) “Parent/child” refers to an inheritance (ISA) relationship; but the design you are describing uses HASA relationships between objects. So far, so good.

        Now, let’s look at the simple example:

        sub some_method { my $self = shift; $self->get_some_framework->get_logger->log_trace("Hello, world!"); }

        The problem with this is that it breaks encapsulation. Specifically, SomeFramework::SomeSubsystem::some_method has to know not only the public interface of the SomeFramework object (which it owns), but also the private implementation detail that a SomeFramework object provides logging via a SomeFramework::Logger object. (Otherwise it won’t know how to call the log_trace method.)

        What if you later decide to change the way SomeFramework implements trace logging? You will have to change the method calls in classes such as SomeFramework::Subsystem, which ought not to be affected by such details. That’s a poor design, because it makes the code brittle. Better to access a single SomeFramework method and let the SomeFramework class delegate its implementation. For example, if file “SomeFramework.pm” is changed as follows:

        # SomeFramework.pm package SomeFramework; use Moose; use MooX::ProtectedAttributes; protected_has logger => ( is => 'ro', isa => 'SomeFramework::Logger', ); sub log_trace { my ($self, $msg) = @_; $self->logger->log_trace($msg); } 1;

        then SomeFramework::Subsystem::some_method can be defined like this:

        sub some_method { my $self = shift; $self->get_some_framework->log_trace("Hello, world!"); }

        The goal is for each class to keep its implementation details private and to expose only a public interface to its “clients.” (In a HASA relationship, the “owner” object is a client of the objects it owns.) This promotes encapsulation by de-coupling classes as much as possible. The end result is a design which is simpler, clearer, more flexible, and easier to maintain.

        Hope that helps,

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

Re: Names of attribute accessors (Moose)
by Anonymous Monk on Oct 23, 2015 at 11:16 UTC
    TIMTOWTDI... e.g. in Java naming getters and setters is typically done in camelCase, e.g. getFoo and setFoo, whereas in Perl it's a bit more common to use one method and the parameter count to distinguish between getter and setter, e.g. foo (get) and foo($newValue) (get and set). get_foo and set_foo is fine too though, just more typing.