in reply to Re: Names of attribute accessors (Moose)
in thread Names of attribute accessors (Moose)

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

Replies are listed 'Best First'.
Re^3: Names of attribute accessors (Moose)
by Athanasius (Archbishop) on Oct 26, 2015 at 12:54 UTC

    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,

      Thank you for the explaination!

      In my case it seems to be okay to call methods of objects whose references are accessible as attributes of some "upper" objects. If all these classes are well-documented, it shouldn't lead to any difficulties.

      By the way, Mojolicious has the same apporach, you can often see something like "my $host = $c->req->url->to_abs->host;" in the code of Mojolicious-based applications.

      V.Melnik