Single-File Inheritance

J

Jim Gibson

I am trying to do the Right Thing and write a data-processing program
using lofty Object-Oriented principles, instead of my usual lazy
procedural ways, but I am having trouble with putting multiple class
packages into one file. I started out using just one file for
convenience while I develop a framework, and then was planning on
splitting the file into separate modules once I got the basics working.

I am trying to read some data files that have the same format in the
first 6 lines, then have different data records in subsequent lines. I
thought I would write a parent class that reads the header lines, then
child classes to read and parse subsequent lines.

Here is the result:

% cat single.pl
#!/usr/local/bin/perl
use strict;
use warnings;

my $file = ConfigFile->new(); # line 5

package DataFile;

sub new
{
my($class) = shift;
my $self = {};
bless $self, $class;
return $self;
}

package ConfigFile;
our @ISA = qw( DataFile );


% perl single.pl
Can't locate object method "new" via package "ConfigFile" at single.pl
line 5.


% perl -v
This is perl, v5.10.1 (*) built for darwin-2level

If I put the packages in separate files, it works (compiles and runs).
I am almost sure that I have done this type of thing before, and I
can't find any documentation that says I can't.

Should I be able to put multiple packages in a single file and use
inheritance for two of those packages?

Thanks.
 
X

xhoster

Jim Gibson said:
% cat single.pl
#!/usr/local/bin/perl
use strict;
use warnings;

my $file = ConfigFile->new(); # line 5
.....

package ConfigFile;
our @ISA = qw( DataFile );
% perl single.pl
Can't locate object method "new" via package "ConfigFile" at single.pl
line 5.

The creation of the @ISA alias occurs at compile time (I think), but the
assignment to @ISA happens only at run time when that line is encountered.

At the time line 5 is executed, the ISA assignment line has not yet
be run in the runtime, so has not yet taken place and @ISA is empty.

You can wrap the @ISA assignment in a BEGIN block to force it to happen at
compile time, or you can move your main code to be below the package code.

Xho

--
-------------------- http://NewsReader.Com/ --------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
D

David Precious

package ConfigFile;
our @ISA = qw( DataFile );

Rather than manually stuff @ISA, I'd use 'parent' with the -norequire
option:

package ConfigFile;
use parent -norequire DataFile;
...
 
J

Jim Gibson

[QUOTE= said:
% cat single.pl
#!/usr/local/bin/perl
use strict;
use warnings;

my $file = ConfigFile->new(); # line 5
....

package ConfigFile;
our @ISA = qw( DataFile );
% perl single.pl
Can't locate object method "new" via package "ConfigFile" at single.pl
line 5.

The creation of the @ISA alias occurs at compile time (I think), but the
assignment to @ISA happens only at run time when that line is encountered.

At the time line 5 is executed, the ISA assignment line has not yet
be run in the runtime, so has not yet taken place and @ISA is empty.

You can wrap the @ISA assignment in a BEGIN block to force it to happen at
compile time, or you can move your main code to be below the package code.[/QUOTE]

Thanks for the explanation. That works.
 
J

Jim Gibson

Seymour J. said:
You have to carve the bird at the joints.


Why? Why not a generic parent class and child classes for header lines
and for each category inferred from the header?

I think that is what I am trying to do. Perhaps I could explain it
better by saying "write a parent class that has methods to read the
header lines, and child classes that have specialized methods to read
and parse subsequent lines." File category is actually derived from the
file name, not the contents of the header lines.
Shouldn't that be

my $file = DataFile->new(); # line 5

I don't think so. DataFile is the parent class and doesn't have any
methods to interpret the data. ConfigFile is a child class that reads
and parses config files (in this simple, made-up example). I won't ever
instantiate an object of the DataFile class. I will instantiate a
ConfigFile object and call new() and open() methods on that object,
which will cause DataFile::new() and DataFile::eek:pen() to be called,
since ConfigFile inherits new() and open() from DataFile.
Could yuou show skeletal versions of the individual files?

The contents of the files are irrelevant for my problem, which happens
before the files are opened. But thanks.
 
J

Jim Gibson

David Precious said:
Rather than manually stuff @ISA, I'd use 'parent' with the -norequire
option:

package ConfigFile;
use parent -norequire DataFile;
...

Thanks. This works:

use parent -norequire, 'DataFile';

although I don't see any advantage over 'our @ISA = ...'
 
J

Jim Gibson

Seymour J. said:
That's what you wrote the first time; it's not what I'm suggesting.

OK. Then I guess I don't understand your suggestion.
Why not have the methods for header lines in child classes?

Because the 6 header lines are the same for all types of files. I have
written an open() method in the parent class that given the file path,
opens the file, reads the first 6 lines, and saves the data therein. It
leaves the file open at the 7th record. The open() method is
implemented in the parent class and inherited by the child classes.

What would be the point in duplicating the open method identically in
all the child classes?

Thanks for your input. Sorry I don't understand your points.
 
R

Rainer Weikusat

Ben Morrow said:
It is, by design, equivalent to

BEGIN { our @ISA = "DataFile" }

and the fact the assignment happens at compile time can be important.

For the few circumstances where keeping a class in the 'wrong' file is a
good idea, I wouldn't bother with 'parent' and would just use the BEGIN
above. 'parent' is useful in the normal case, where you need to load the
parent class as well as inherit from it.

For a lot of not entirely trivial code I have written, this is
actually the common case and not because classes reside in files which
won't be found by the search algorithm which happens to be used by
'use' but because the dependencies among the classes themselves are
too complex to enabling loading modules defining parent classes from
the files defining dependent classes. In this case, I usually load all
modules from the source files containing the main program and declare
dependencies in the classes themselves.

Morale: That someone has never seen code where separating loading of
modules and declaring inheritance relationships was necessary doesn't
exactly make that someone someone who is qualified to judge that this
separation doesn't make sense.

Minor gems:

{
no strict 'refs';
# This is more efficient than push for the new MRO
# at least until the new MRO is fixed
@{"$inheritor\::ISA"} = (@{"$inheritor\::ISA"} , @_);
};

In plain English, this means 'this is a workaround for a bug in some
other code' [which isn't used by default]. And this is - of course -
an atrociously indefficient way to reinvent unshift.

[rw@sapphire]~ $perldoc -f unshift
unshift ARRAY,LIST
Does the opposite of a "shift". Or the opposite of a
"push", depending on how you look at it. Prepends list to the
front of the array, and returns the new number of elements in
the array.


unshift(@ARGV, '-e') unless $ARGV[0] =~ /^-/;

Note the LIST is prepended whole, not one element at a
time, so the prepended elements stay in the same order.
Use "reverse" to do the reverse.
 
R

Rainer Weikusat

[...]
{
no strict 'refs';
# This is more efficient than push for the new MRO
# at least until the new MRO is fixed
@{"$inheritor\::ISA"} = (@{"$inheritor\::ISA"} , @_);
};

In plain English, this means 'this is a workaround for a bug in some
other code' [which isn't used by default]. And this is - of course -
an atrociously indefficient way to reinvent unshift.

It isn't unshift at all, of course, I just mistakenly assumed that it
had to be doing something other than adding elements to the end of a
list ...
 
R

Rainer Weikusat

Rainer Weikusat said:
[...]
For the few circumstances where keeping a class in the 'wrong' file is a
good idea,

Here's what the Camel book has to say on "using WRONG files" (NB: A
copy of that resides on my bookshelf at home some please spare me the
anti-scientific outcry this time):

Sometimes folks are surprised that including a class in @ISA
doesn't require the appropriate module for you. That's because
Perl's class system is largely orthogonal to its module
system. One file can hold many classes (since they're just
packages), and one package may be mentioned in many files.

But I guess that has also been 'deprecated' by someone meanwhile ...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,007
Latest member
obedient dusk

Latest Threads

Top