Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Refactoring Perl #8 - Replace Method with Method Object

by agianni (Hermit)
on Nov 12, 2007 at 19:44 UTC ( #650358=perlmeditation: print w/ replies, xml ) Need Help??

You have a long method that uses local variables in such a way that you cannot apply Extract Method

Turn the method into its own object so that all the local variables become fields on that object. You can then decompose the method into other methods on the same object.

(Fowler, p. 135)

Here's how it looks:

sub validate{ my $self = shift; my $errors_ref; my $validation_profile = $self->get_validation_profile(); my $input = $self->get_input(); my $result = $self->process_input_against_profile( $input => $validation_profile ); if ( $result->{success} ) { return { success => 1 }; } else { $errors_ref = $self->process_errors( $result ); return { success => 0, errors => $errors_ref, }; } }

becomes

sub validate{ my $self = shift; return Validator->new( $self->get_input )->validate(); } package Validator; sub validate{ my $validator = shift; my $result = $validator->process_input_against_profile(); if ( $result->success() ) { return { success => 1 }; } else { return { success => 0, errors => $result->errors(), }; } }

Get the code

I put together my own example because Fowler's own is contrived and not directly applicable. In his own words:

A proper example of this requires a long chapter, so I'm showing this refactoring for a method that doesn't need it.

Thanks Martin :)

My example may not be the best application of this refactoring pattern either, but I find this to be the most common reason for using this pattern and I think the example is a little more applicable to the context Perl developers are used to working in. In this case, I have a cluttered class representing a Web application that does a lot of work and I have utility methods to help with the validation. I don't really need those in there, so I make a new class to handle the validation and put my methods over there.

perl -e 'split//,q{john hurl, pest caretaker}and(map{print @_[$_]}(joi +n(q{},map{sprintf(qq{%010u},$_)}(2**2*307*4993,5*101*641*5261,7*59*79 +*36997,13*17*71*45131,3**2*67*89*167*181))=~/\d{2}/g));'

Comment on Refactoring Perl #8 - Replace Method with Method Object
Select or Download Code
Re: Refactoring Perl #8 - Replace Method with Method Object
by agianni (Hermit) on Nov 12, 2007 at 22:00 UTC
    I might add that this pattern is also known in the Perl community as Oh, there's a CPAN module that does that?; i.e. you write twenty lines of code for a subroutine only to find that there's a CPAN modules that does what you were hoping for:
    sub do_that_thing{ # some code # more code # even more code }

    becomes:

    sub do_that_thing{ return &That::CPAN::Module::subroutine( $some, $arguments ); }
    perl -e 'split//,q{john hurl, pest caretaker}and(map{print @_[$_]}(joi +n(q{},map{sprintf(qq{%010u},$_)}(2**2*307*4993,5*101*641*5261,7*59*79 +*36997,13*17*71*45131,3**2*67*89*167*181))=~/\d{2}/g));'
Re: Refactoring Perl #8 - Replace Method with Method Object
by jdporter (Canon) on Nov 14, 2007 at 15:43 UTC

    I was slightly disappointed that you didn't implement Validator as a true method object. It would be possible to do so using overloading, or, more clumsily, blessed subs. In fact, your example doesn't demonstrate much advantage to having Validator be a class at all; it could almost as well have been a simple package containing some subroutines.

      Interesting, I was unaware of such a formal definition of a method object; I was simply following Fowler's lead. I'm not sure he was referring to method objects in that sense. Now that you bring it up, though, that's an interesting programming concept. Looking around, I found this interesting paper on the topic of writing functors in Perl. I imagine that Class::Prototyped would be useful in implementing this in Perl

      Like Fowler's example, though, mine is also probably too simple to show the usefulness of this refactoring pattern, under which the use of a functor or the like would actually be useful. As I go through Fowler's refactoring patterns, I find that many of them are perfectly applicable to non-OO Perl. In fact, the example I gave in my follow-up about CPAN modules does just that.

      I most often employ this pattern when I am trying to avoid the blob anti-pattern and I want to get a bunch of related methods out of my class. It can also be useful if I'm working in an OO framework and I want to swap in a different class. I would implement it like this:

      sub some_method{ return shift->that_package()->new(); }

      and later:

      sub that_package{ return 'Some::Package'; }

      Then when I'm sub-classing in my framework, I can override package to provide the name of the package I want to actually use. This will generally be a sub-class or sibling of Some::Package and have the same interface.

      perl -e 'split//,q{john hurl, pest caretaker}and(map{print @_[$_]}(joi +n(q{},map{sprintf(qq{%010u},$_)}(2**2*307*4993,5*101*641*5261,7*59*79 +*36997,13*17*71*45131,3**2*67*89*167*181))=~/\d{2}/g));'

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others wandering the Monastery: (9)
As of 2014-09-17 20:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (98 votes), past polls