Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Re^2: How Large Does Your Project Have To Be to Justify Using Moose? (modular)

by Boldra (Deacon)
on Oct 13, 2011 at 08:03 UTC ( #931156=note: print w/ replies, xml ) Need Help??


in reply to Re: How Large Does Your Project Have To Be to Justify Using Moose? (modular)
in thread How Large Does Your Project Have To Be to Justify Using Moose?

If I've understood you correctly, you make these three arguments against Moose:

  1. It encourages users to create "glorified hashes".
  2. Other techniques can give you syntax-checking of attributes.
  3. Moose type-checking can create more problems than it solves.

I don't understand your argument against "glorified hashes." Are you criticising Moose for being unable to instantiate other kinds of references (array refs, scalar refs etc)?


Comment on Re^2: How Large Does Your Project Have To Be to Justify Using Moose? (modular)
Re^3: How Large Does Your Project Have To Be to Justify Using Moose? (modular)
by tye (Cardinal) on Oct 22, 2011 at 21:37 UTC

    I'm criticizing Moose for nearly discouraging the designing of a class based on just the interface and not exposing the attributes (which should be internal to the implementation). Even if you don't have Moose generate accessors, then it generates a constructor that still exposes the attributes. And if you don't generate accessors then you can't make use of many roles.

    I'm starting to realize that there are people (I can't tell how many / how common yet) that can't even really conceive of designing a class without jumping straight to building a list of attributes, even after you explain to them that they should design the interface before designing the attributes (which are part of the implementation). Moose certainly encourages that mindset.

    And I keep finding more and more instances of bad class design that look to me to be primarily due to way too much focus on an object as "just a bag of attributes".

    When I design a class, the constructor(s) are just another part of the interface. They don't expose the internal attribute names. So I don't understand the mindset of making the constructor just be a list of attribute-name and attribute-value pairs, even if that is just the default that can be overridden. It leads to classes that poorly encapsulate the internal details. It is anti-modular.

    Worst of all, it encourages making classes that expose data structure which encourages encoding class-specific behavior outside of the class (the most common sign of "bad design" that I keep finding and then looking for the source of and tracking back to a class design concentrating on a list of attributes).

    # One reasonable constructor interface for a square: Square->new( center => [ $x, $y ], width => $w );

    But I'd likely implement the square as a list of 4 x/y coordinates. And I'm not sure how I want to store the coordinates (as pairs or as parallel arrays or ?). I don't even think too much about how I'm going to implement the square's data when I'm defining the interface for the square.

    I design the interface first and come up with my first stab at what attributes to use to implement it later. And changing my implementation shouldn't force me to change my interface.

    Providing so many ways to plumb attributes straight into the class's interface actually makes it hard to keep the interface separate. And then there are the add-on tools that become useless or much less useful if you don't plumb your attributes to the interface.

    I'm coming to realize that the benefit of modular programming practices is that they are slightly inconvenient and thus force you to see the lack of perfection in your first thought of a design and encourage you to spend some time re-thinking and iterating the design, often resulting in you improving the design (and sooner rather than later and thus saving much larger spans of time / effort).

    It should be slightly inconvenient to delegate a method. That way, you don't just blithely delegate way too many methods and end up delegating methods that are quite inappropriate and are going to lead to problems (I've seen several instances of that just this week due to old uses of inheritance). It should be more inconvenient to provide a bare accessor to an attribute. You should only provide accessors when they really make sense for the interface, completely ignoring the current implementation.

    The construction of an accessor should be motivated by the interface. When you go to implement that accessor, at that point you might decide that it also makes sense to make an attribute that simply mirrors the data plumbed via that accessor. You should not follow the design decision about including some attribute with trying to decide whether or not you want to have an accessor for it.

    Otherwise, your interface is being driven by your implementation, and that is a perfect example of anti-modular design.

    - tye        

      design the interface first

      Bravo!

      I take that one step further. Design the code that will use the class first, and the interface will, for the most part(*), fall out of the use case provided by that design.

      (*) Once the interface has been implemented for the specific, real-world use case, it is often possible to see small changes that can be applied to generalise it for a wider set of use cases, without compromising its effectiveness and efficiency for the one real use case. These are then a no-brainer to adopt.

      From my observations, the problem is that far to often people set out to define a class without having some real-world use case to satisfy. The result is that they end up trying to write a generic interface to satisfy all (their) conceived use cases, with the result that they become overburdened with configurability and flexibility and end up being over-engineered and heavy. Carrying the weight of generality that is never exercised. Non-optimal for all use cases.

      I don't lay this designer problem at the door of tool-sets like Moose, but it certainly does not discourage it.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        So, the one true book(s) to teach you how to design is? :)
      Even if you don't have Moose generate accessors, then it generates a constructor that still exposes the attributes.

      By default, yes it does. But it is easily remedied.

      has 'foo' => ( is => 'bare', # no accessors init_arg => undef, # no longer accessible via constructor );

      And if you don't generate accessors then you can't make use of many roles.

      Not sure what that even means because roles and accessor generation have nothing to do with one another. Care to clarify this?

      I'm starting to realize that there are people (I can't tell how many / how common yet) that can't even really conceive of designing a class without jumping straight to building a list of attributes, even after you explain to them that they should design the interface before designing the attributes (which are part of the implementation). Moose certainly encourages that mindset.

      Again, I really fail to see how Moose encourages this. Just because Moose makes it easy to build attributes, that in no way forces not think about the interface first. Moose is just a tool to help you write your classes, it is still your responsibility to actually design the classes.

      # One reasonable constructor interface for a square: Square->new( center => [ $x, $y ], width => $w );
      But I'd likely implement the square as a list of 4 x/y coordinates. And I'm not sure how I want to store the coordinates (as pairs or as parallel arrays or ?). I don't even think too much about how I'm going to implement the square's data when I'm defining the interface for the square.

      So, again, Moose doesn't prevent you from doing this.

      package Square; use Moose; has 'top_left' => ( init_arg => undef, accessor => '_top_left' ); has 'top_right' => ( init_arg => undef, accessor => '_top_right' ); has 'bottom_left' => ( init_arg => undef, accessor => '_bottom_left' ) +; has 'bottom_right' => ( init_arg => undef, accessor => '_bottom_right' + ); sub BUILD { my ($self, $params) = @_; my $center = $params->{center}; my $width = $params->{width}; $self->_construct_coordinates_from_center_and_width( $center, $wid +th ); }
      What this buys you over doing it by hand is basically automated (private) accessor construction and proper constructor initialization for any subclasses (via BUILD). It is also pretty concise and (with the exception of init_arg => undef which is a little odd) is pretty easy to understand (once you know Moose of course).

      -stvn

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://931156]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (10)
As of 2014-09-02 17:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite cookbook is:










    Results (25 votes), past polls