The problem with the constructor is the ref $proto || $proto line. What's that for? It allows you to do this:
my $object = Class->new;
my $object2 = $object->new;
The problem is, unless you document why you need that, it's very unclear. Is $object2 a brand-new, clean object with no relation to the first object? If so, why are you calling new as an instance method?
Some people might think it's supposed to clone the first object. If so, is it a shallow or deep copy? Is there a reason in the code to clone the first object?
If it creates a brand new object, either only allow new as a class method or, if you don't have the class handy (maybe it's generated from a factory), then document it like this:
my $object2 = (ref $object)->new;
If it really is a clone, create a clone method and avoid the confusion.
Since few people can agree on the semantics of $object->new, it's a source of confusion. That being said, use it if you need it, but make it clear why you're doing this.
In fact, this has been a source of enough confusion and argument that I removed most references to this in the Perl docs.
| [reply] [d/l] [select] |
The disapproved manner ... check this thread A few Perl OOP questions. for more info. I'll leave it at that. ;-)
The caller thing - I'm not sure about speed. But I don't see caller used very often, so it's probably not well understood. That said, I merely mentioned the way I'd normally approach the problem and did not mean to imply that yours was inferior. There's "clever" and "too clever". Where the line is drawn, however, depends on one's experience ;-)
Here's what I think a reasonable usage of this paradigm would look like from the caller's perspective. Note that this is not intended to be the only reasonable usage, just an example of one reasonable usage:
# here's the pre portion...
my $count;
my %data;
IterateOver::file({
loop => sub {
if (/^(.*?)\s*=\s*(.*?)\s*(?:#.*)$/)
{
$data{$1} = $2;
}
},
$some_file
);
# post goes here...
The only purpose of having a pre and a post is when you have loops inside of loops - so the pre and post are executed prior to the loop commencing, and after the loop is completed, respectively.
Thus, the example:
my @files = ( ... );
my $count;
my %data;
IterateOver::files({
pre => sub {
print "Starting on file $_.\n";
$count = 0;
},
loop => sub {
if (/^[^#]/ and /^(.*?)\s*=\s*(.*?)\s*(?:#.*)$/)
{
$data{$1} = $2;
++$count;
}
},
post => sub {
print "Found $count keys in $_.\n";
print "Total unique keys so far: ", scalar keys %data, "\n";
},
@files
);
Of course, this is completely not an object. To be honest, right-tool-for-right-job does not tell me that OO is the best way to do this in perl (and I'm an OO bigot ... at least from my C++ days!). Closures are way easier to use for this.
Don't get me wrong, I've done this in an object in perl. But that's because I was part of a framework, and my framework was already object oriented (oddly enough, this is the third time today I've talked about TaskManagers and Tasks). I have a package that does iteration over some common data, and then calls object methods (not passed in but predefined) as hooks into various parts of the loop - including pre-filter, filter, and post-filter. There's no need to pass in callbacks because the loop function is inside another module dedicated to this task. It looks something like this (see above for the layout of Task):
To use this, you derive from TaskFilter your own package. And then you have a $self to use - and that $self really is your filter object. As in your filter object. Thus it makes perfect sense to modify object variables inside object methods, so you can do whatever you want with it - update %$self, etc.
Does that help? | [reply] [d/l] [select] |
/me mumbles "I knew getting into this OOP stuff would snowball."
I'm going home. I'll re-read your reply a few dozen times tomorrow :) I printed out one of the links to review--Thanks!
| [reply] |
I think I'm gaining some understanding...
Your second code example works because the anonymous subs are within the same lexical scope; therefore, there is no need to store the variables they work with separately (in $self, like I did). Correct? I like that.
The last block of code looks similar to mine, one of the differences being that you only call $self->filter($r) within the loop. Wouldn't the code within filter() be same difference to what I'm doing? That is, I still need an increment and a hashing, so filer() would contain ++$self->{count}; and so forth?
I believe I'm lost somewhere in between "This is only natural inside a single object, however your callbacks are all external to your object, so that doesn't make a bunch of sense to me." and "As in your filter object. Thus it makes perfect sense to modify object variables inside object methods, so you can do whatever you want with it." The latter statement says to me: "There are no closures (external thingies) involved, therefore everything happens within your methods," which makes me think "Well then, how do i keep this dynamic? Each filtering process is going to be 80% common, but there is some variation."
Thanks for your time.
P.S. I am reading through perltoot, perlobj, a few tutorials on this site, and the llama.
| [reply] |
Your second code example works because the anonymous subs are within the same lexical scope; therefore, there is no need to store the variables they work with separately (in $self, like I did). Correct? I like that.
Precisely. And, if also notice, that second code example also is extremely similar to the way you're using your Hasher, but with a lot less overhead, more intuitive (I think), and no usage of $self.
The last block of code looks similar to mine, one of the differences being that you only call $self->filter($r) within the loop. Wouldn't the code within filter() be same difference to what I'm doing? That is, I still need an increment and a hashing, so filer() would contain ++$self->{count}; and so forth?
I see no evidence of not gaining understanding (understand that! ;-}); you nailed this one as well. The difference is that in the last block of code, the expectation is not that you'll give me coderefs from which I can call back to do stuff, but that you'll use inheritance to create a brand new package, and in that package, you'll name certain methods the names that I expect, and I'll call those as object methods at the appropriate places.
I believe I'm lost somewhere in between "This is only natural inside a single object, however your callbacks are all external to your object, so that doesn't make a bunch of sense to me." ...
Remember, like perl, English is context sensitive. (Sorry - I know it's the other way around, but it was funnier this way.) The context of this statement was talking about using "$self" inside callbacks - this is rarely done. Callbacks are generally done via closures in perl, and, because they're closures, they have access to all lexical variables at the point of their creation - if you need data to persist past the function call, you simply set it in your closure. For another example, see the File::Find module which uses this idea for the wanted routine.
... and "As in your filter object. Thus it makes perfect sense to modify object variables inside object methods, so you can do whatever you want with it." The latter statement says to me: "There are no closures (external thingies) involved, therefore everything happens within your methods," which makes me think "Well then, how do i keep this dynamic? Each filtering process is going to be 80% common, but there is some variation."
Dynamic - depends on how you want to do it. There are so many ways to do it that I can't really tell you what the best way is - it will depend on what you're trying to do, and the framework you're doing it in (both framework as in any object frameworks you may be using, such as POE, and framework as in how the rest of your code is laid out, the context, if you will.)
One way is to have different packages derived from your Filter package. You can then instantiate each one ("my $f = FilterThis->new()") and then execute it.
You can then find commonalities between each of your filter packages and push them into a new base class such that FilterThis is derived from CommonFilter which itself is derived from Filter. This is fine - I do this, and my Filter is actually dervied from Task, so you can keep going as much as you want. It's a little convoluted as a first foray into OO, but trust me, this provides a lot of flexibility.
Or, you can go simple - a halfway between procedural and OO. (Again, I may be an OO bigot, but this is often the right solution, so don't let my moniker scare you away from this option even while trying to learn OO.) This is to create a few packages derived from Filter, but each one would be drastically different from each other - any commonalities have been put into the filter function for each group. For example, if you were filtering a bunch of websites and a bunch of files, you could have three filter packages: FilterWebHTML, FilterWebXML, and FilterFlatFile. Then, when you create each one, you pass in the variable that applies, e.g., my $f = FilterWebXML->new('http://www.perlmonks.org/index.pl?displaytype=xml;node_id=3333'). You can create lots of each type of object which filters differently from each other, but have the commonality split out based on logic (HTML, XML, Flat files are all filtered differently) vs parameters (the location of each file).
my @htmls = qw( ... );
my @xmls = qw( ... );
my @flats = qw( ... );
my @filters;
push @filters, map { FilterWebHTML->new($_) } @htmls;
push @filters, map { FilterWebXML->new($_) } @xmls;
push @filters, map { FilterFlatFile->new($_) } @flats;
$_->perform_filter() for @filters;
And in the new method, you just have to store the filename/URL to be filtered in $self somewhere.
| [reply] [d/l] [select] |
s/llama/alpaca/; # thanks bart
| [reply] |