Документация
HTML CSS PHP PERL другое

Section 16.3.  Blessing Objects

 
Previous
Table of Contents
Next

16.3. Blessing Objects

Never use the one-argument form of bless.

The built-in bless function associates a referent of some kind (typically a hash, an array, or a scalar) with a particular class, thereby converting the raw data type into an object. Normally, bless takes two arguments: a reference to the referent that is to become the object, and a string naming the desired class of that object. However, the second argument is actually optional, and defaults to the current package name.

Developers will occasionally attempt to save a miniscule amount of effort by writing a constructor like so:

    package Client;
    use Class::Std::Utils;
    {
        my %client_num_of;

        sub new {
            my ($class, $arg_ref) = @_;

            my $new_object = bless anon_scalar( );
            # (One-arg bless saves typing!)

            $client_num_of{ident $new_object} = $arg_ref->{client_num};

            return $new_object;
        }

        # etc.
    }

Unfortunately, the half a second they save that way can lead to much more substantial amounts of time lost when they have to work out why objects of the following derived class don't work correctly:

    package Client::Corporate;
    use base qw( Client );
    use Class::Std::Utils;
    {
        # Attribute...
        my %corporation_of;

        sub new {
            my ($class, $arg_ref) = @_;

            # Call base class constructor to allocate and initialize object...
            my $new_object = $class->SUPER::new($arg_ref);

            # Initialize derived classes own attributes...
            $corporation_of{ident $new_object} = $arg_ref->{corp};

            return $new_object;
        }

        # etc.
    }

What they will eventually discover is that calls like:


    Client::Corporate->new(\%client_data);

are actually producing objects of class Client, rather than of the requested subclass. That's because Client::Corporate::new( ) calls the Client::new( ), which does a one-argument bless, which blesses into the current package. And, inside Client::new( ), the current package is always Client.

Using the two-argument form of bless prevents the problem, as you're always telling the function explicitly which class the object belongs to:


    package Client;
    use Class::Std::Utils;
    {
        my %client_num_of;

        sub new {
            my ($class, $arg_ref) = @_;

            my $new_object = bless anon_scalar( ), $class;
            
# (Two-arg bless saves debugging!)
$client_num_of{ident $new_object} = $arg_ref->{client_num}; return $new_object; }
# etc.
}

However, when using the two-argument form of bless, it's important to avoid explicitly stringifying the class name:

    my $new_object = bless anon_scalar( ), "$class";

Always call bless with a copy of the actual first argument the constructor was passed, like so:


    my $new_object = bless anon_scalar( ), $class;

Under Perl 5.8 and later, bless is able to detect when an object reference is mistakenly used as a class name. That usually happens when a constructor is being called as an object method, rather than a class method:

    my $back_up = $existing_client->new( );    # The benefits of human cloning

If bless always just quietly stringified its second argument (as it used to do, prior to Perl 5.8), then inside the constructor the object reference in $class would be stringified to something nasty like 'Client::Corporate=SCALAR[0x12b37ca]', which would then be used as the name of the class into which the new object was actually blessed. That is unlikely to be what the client code expects or wants[*].

[*] Although it's not impossible that that was the desired effect: several advanced OO techniques make use of a similar trick to generate unique class names for singleton objects at run time. The gruesome details of such techniques are left to the reader's imagination.

Recent versions of Perl (5.8 and later) prevent such calamities by checking whether the second argument to bless is already a string, and complaining (fatally) if it isn't. Explicitly stringifying that second argument before it's passed will thwart that important sanity check.

Of course, the problem still exists under earlier versions of Perl. To avoid it, you can add a statement at the start of the destructor to explicitly detect when the "class" is actually a reference to an object:


    sub new {
        my ($class, $arg_ref) = @_;

        croak 'Constructor called on existing object instead of class'
            if ref $class;

        my $new_object = bless anon_scalar( ), $class;

        $client_num_of{ident $new_object} = $arg_ref->{client_num};

        return $new_object;
    }

    Previous
    Table of Contents
    Next
    © 2000- NIV