How can I call a Perl package I define in the same

2019-02-06 01:02发布

问题:

I need to define some modules and use them all in the same file. No, I can't change the requirement.

I would like to do something like the following:

{
    package FooObj;

    sub new { ... }

    sub add_data { ... }
}

{
    package BarObj;

    use FooObj;

    sub new { 
        ... 
        # BarObj "has a" FooObj
        my $self = ( myFoo => FooObj->new() );
        ...
    }

    sub some_method { ... }
}

my $bar = BarObj->new();

However, this results in the message:

Can't locate FooObj.pm in @INC ...
BEGIN failed...

How do I get this to work?

回答1:

Drop the use. Seriously.

use tells perl to read in the code from another file, which you don't need to do because the code is in the same file.



回答2:

Unless I'm trying to create a private package that no one should know about, I put one package per file. That solves the problem. But, let's put them in the same file.

The use loads a file and calls the import method in that package. It's really only incidently that its argument looks like a module name. It's looking for the file. If the file is not there, it barfs.

You can do this, where BarObj assumes that FooObj is already there:

{
    package FooObj;
    sub new      { bless { _count => 0 }, $_[0] }
    sub add_data { $_[0]->{_count}++ }
}

{
    package BarObj;
    use Data::Dumper;

    sub new {
        bless { myFoo => FooObj->new }, $_[0];
        }

    sub foo         { $_[0]->{myFoo} }
    sub some_method { print Dumper( $_[0] ) }
}

my $bar = BarObj->new;
$bar->some_method;

If you need to interact with a package (and that's all it is: not a module or an object), you just need to have it defined before you want to use it. If you need to import something, you can call the import directly:

FooObj->import( ... );

Suppose there's something from FooObj that you want to import (but not inherit!), you call import directly with no loading;

{
    package FooObj;
    use Data::Dumper;
    sub new      { bless { _count => 0 }, $_[0] }
    sub add_data { $_[0]->{_count}++ }

    use Exporter qw(import);
    our @EXPORT = qw(dumper);
    sub dumper   { print Dumper( $_[0] ) }
}

{
    package BarObj;
    FooObj->import;

    sub new {
        bless { myFoo => FooObj->new }, $_[0];
        }

    sub foo         { $_[0]->{myFoo} }

    # dumper mixin, not inherited.
    sub some_method { dumper( $_[0] ) }
}

my $bar = BarObj->new;
$bar->some_method;


回答3:

By convention we put one package in one file and name them the same thing, but that is just for convenience. You can put multiple packages in a single file. Since they are already loaded, you do not need to use use.

You also do not need to create special scoping for the packages, as the package keyword takes care of that. Using the braces does help with scoping of our variables. So you don't strictly need those brace blocks, but they're a good idea.

use uses a package naming convention to find the appropriate file to load. The package keyword inside the module defines the namespace. And the import functions handle the package loading (generally inherited from Exporter).

#!/usr/bin/perl

use strict;
use warnings;

package FooObj;

sub new
{
        my $this  = shift;
        my $class = ref($this) || $this;
        my $self  = {};
        bless $self, $class;
        $self->initialize();
        return $self;
}

sub initialize { }
sub add_data   { }

package BarObj;

#use FooObj; <-- not needed.

sub new
{
        my $this  = shift;
        my $class = ref($this) || $this;
        my $self  = { myFoo => FooObj->new() };
        bless $self, $class;
        $self->initialize();
        return $self;
}
sub initialize  { }
sub some_method { }
sub myFoo       { return $_[0]->{myFoo} }

package main;
use Test::More;
my $bar = BarObj->new();
isa_ok( $bar,        'BarObj', "bar is a BarObj" );
isa_ok( $bar->myFoo, 'FooObj', "bar->myFoo is a FooObj" );
done_testing();

__DATA__

ok 1 - bar is a BarObj isa BarObj
ok 2 - bar->myFoo is a FooObj isa FooObj
1..2


标签: perl oop