Modules and Object Classes Some Background and Case Study Examples

helmetpastoralΛογισμικό & κατασκευή λογ/κού

13 Δεκ 2013 (πριν από 3 χρόνια και 6 μήνες)

234 εμφανίσεις

Modules and Object Classes


Some Background and Case Study Examples



Bill Cowan

Metagenix, Inc.

www.metagenix.com

919 / 490
-
0034

bcowan@metagenix.com



For any comments on my approach to Perl O
-
O style,

please
objec
t

politely…

Table of Contents

Table of Contents

................................
................................
..........................

2

Background: References

................................
................................
..............

3

Scalar References

................................
................................
......................

3

Array References

................................
................................
.......................

3

Anon
ymous Array References:

................................
................................
...

3

Hash References

................................
................................
.......................

5

Anonymous Hash References:

................................
................................
...

5

Background: Perl Modules

................................
................................
............

6

Example of Simple Module

................................
................................
.........

6

Calling Program

................................
................................
.........................

7

Comments about Sample Module

................................
................................
..

9

Example of Module with POD

................................
................................
......

10

Perl and Classes/Objects

................................
................................
.............

13

The Basi
cs …

................................
................................
...........................

13

Example of Simple Class from perltoot

................................
........................

14

How to Represent an Object

................................
................................
........

17

Categories for Methods

................................
................................
................

18

Expanded Example with Inherited

Utility Class

................................
............

19

Sample Methods from Utility Class

................................
..............................

23

AUTOLOADed Data Assessors

................................
................................
...

27

Candidates to Consider for Utility Class

................................
.......................

29

Producti
vity Thoughts to Code Classes

................................
.......................

30


Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
3

of

30

919 / 490
-
0090

Background: References

Scalar References

DB<1> $Scalar = 'abcd'

DB<2> $ScalarRef =
\
$
Scalar


DB<3> print $ScalarRef

SCALAR(0xdef3bc)


DB<6> print $Scalar, ' ',
${
$ScalarRef
}

abcd abcd

Arra
y References

DB<8> @Array = qw( A B C D);

DB<9> $ArrayRef =
\
@
Array;


DB<10> print $ArrayRef, ' ', join('|',
@{
$ArrayRef
}
)

ARRAY(0x9b6940) A|B|C|D


DB<11> print $ArrayRef, ' ', join('|',
@{
$ArrayRef
}
)

ARRAY(0x9b6940) A|B|C|D


DB<13> print $ArrayRef
-
>[1]
, ' ', $ArrayRef
-
>[3]

B D

Anonymous Array References:

DB<16> $ArrayRef =
[
'a', 'b', 'c', 'd'
]


DB<18> print
@{
$ArrayRef
}
, ' ', $ArrayRef
-
>[
1
]

abcd b


Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
4

of

30

919 / 490
-
0090

Doc Reference
:


perlref

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
5

of

30

919 / 490
-
0090

Hash References

DB<19> %Hash = (A => '1', B => '2')

DB<20> $HashRef =
\
%
Hash


DB<24> print $HashRef
-
>{A},

' ', keys(
%$
HashRef)

1 AB


DB<25> print $HashRef
-
>{A},

' ', keys(
%{
$HashRef
}

)

1 AB


DB<26> print $HashRef
-
>{A},

' ', keys(
%$
HashRef )

1 AB

Anonymous Hash References:

DB<27> $HashRef =
{
A => '1', B => '
2'
}


DB<29> print $HashRef, ' ', join('|',
%{
$HashRef
}
)

HASH(0xdeff38) A|1|B|2


DB<30> print $HashRef
-
>{A},

' ', keys(
%{
$HashRef
}

)

1 AB


Note different ways to do de
-
referencing on the reference variables:



%{

$HashRef
},


$HashRef
-
>{
'A'
}


Cautio
n
: Until you are comfortable with references and de
-
referencing
process, do not start writing classes and objects!


Doc Reference
:


perlref

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
6

of

30

919 / 490
-
0090

Background: Perl Modules

Example of Simple Module

#
--

MODULE: HandyDandyLib.pm

package HandyDandyLib;

use strict
;


use vars

qw(@ISA @EXPORT @EXPORT_OK



%EXPORT_TAGS $VERSION);


use Exporter();

@ISA = qw(Exporter);


@EXPORT = qw(Trim Ltrim);

@EXPORT_OK = qw(TrimHashValues);

#
--

optional
functions to export.


%EXPORT_TAGS = (VARS => [ @EXPORT_OK ]
);

$VERSION = '';


sub Trim

{


#
--

Parameters:


my( $StringParm #
--

string to process


) = @_;



$StringParm =~ s/^
\
s+//;


$StringParm =~ s/
\
s+$//;


return( $StringParm );

} #
--

end of Trim function

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
7

of

30

919 / 490
-
0090

Example Module Continued.


sub

TrimHashValues

{


#
--

Parameters:


my( $HashRef, #
--

in/out: hash ref to trim.


)=@_;


....


return(1);

} #
--

end of TrimHashValues function.


sub GetPgmRevNbr

{


#
--

Function parameters:


my ($RevString, #
--

Input revision
string.


) = @_;


....


return($1);

} #
--

End of GetPgmRevNbr.

1; #
--

end of module


Calling Program

#
Default

imported entry points.

use HandyDandyLib;


# &Trim was in @EXPORT

$String = &Trim($Input) . subtr($Name, 1, 5);

------------------

# E
xplicit request to
import specific

entry point.

use HandyDandyLib
qw(TrimHashValues)
;


# &TrimHashValues was in @EXPORT_OK.

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
8

of

30

919 / 490
-
0090

%NewHash = &TrimHashValue(
\
%InHash);


Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
9

of

30

919 / 490
-
0090

Comments about Sample Module


Points about Module:



"Package HandyDandyLib" to create separate
namespace.



"use vars …" as way to declare package standard package globals.



"use Exporter;" to handle export entry points into caller's namespace.



@EXPORT, @EXPORT_OK to control which entry points to export by
default or by request.




"sub Trim…" to define
each function. Some functions can be exported and
other can remain local to this module.



"1; #
--

end of module" to signal end of this module.


Points about Caller's Code



Note difference in "use" statement and how that relates to which function
names are l
isted in @EXPORT and @EXPORT_OK.



You know that a function is defined in a module (e.g. a .pm file), because
you visually verified that function existed. But Perl tells you "undefined"
function. Check the @EXPORT and @EXPORT_OK arrays.



Also remember that
undefined functions are runtime error (not found at
compile time). You have forgotten the "use" command.



If Perl can not locate the .pm file for the module, then do "
perl
-
V
" to
check your list of directories being searched for .pm files.



Another good tric
k in the debugger is to do "
x
\
%INC"

to display module
name and what directory it came from off list of directories.






Reference
:


perlmod, perl, perlsub, perlmodlib




Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
10

of

30

919 / 490
-
0090

Example of Module with POD

See Handout Available with Generated HTML Documentation


#
----------------------------------------------------

# MODULE: HandyDandyLib.pm

#
----------------------------------------------------


#
----

START of Plain Old Documentation (POD)
--------

=head2 NAME


B<HandyDandyLib>
-

A function lib
rary defined in the
HandyDandyLib.pm module.


=head2 SYNOPSIS



use HandyDandyLib;


B<Functions:>



Trim
--

Trim leading and trailing whitespace from scalar.


TrimHashValues
--

Trim leading/trailing whitespace from a
hash.


GetPgmRevNbr
--

Retu
rn the program revision number.


B<Prerequisites:> Perl 5.002


=head2 DESCRIPTION


B<HandyDandyLib> provides a library of simple, common utility
functions. These functions are application
-
independent.


=head2 SOURCE CONTROL INFORMATION



Source file:

$Archive: /Perl/Misc/HandyDandyLib.pm $

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
11

of

30

919 / 490
-
0090


Last changed by: $Author: Billc $


Date: $Date: 5/13/99 12:02a $


Version: $Revision: 4 $


=head2 DIAGNOSTICS and KNOWN BUGS


None.


=head2 SEE ALSO


None.


=head2 AUTHOR


B
ill Cowan.


=cut


#
--------------

END of Plain Old Documentation (POD)
----------

package HandyDandyLib;

use strict;

<<< SNIP >>>


#
--------------------------------------------------------------
-

=head1

=head2 C<TrimHashValues>
--

Trim leading/trailing whi
tespace
from a hash.



$Result = &TrimHashValues(
\
%HashToTrim);


The C<TrimHashValues> function removes leading and trailing
spaces from all values in a hash. Returns true (one) on
success, and false (zero) on error.


Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
12

of

30

919 / 490
-
0090

Parameters:



\
%HashToTrim
-

A r
eference to the hash to trim.


=cut

sub TrimHashValues { . . . .

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
13

of

30

919 / 490
-
0090

Perl and Classes/Objects

(Material Heavily Stolen from Perl's Documentation Set)

The Basics …

1.

An
object

is simply a reference that happens to know which class it
belongs to.

2.

A
class

is simp
ly a Perl package that happens to provide methods to deal
with object references.

3.

A
method

is simply a subroutine that expects an object reference as the
first argument. For class methods, the package name (e.g. class name) is
expected as the first argume
nt.


Method invocations for Employee class:



Class

method using Employee class name, e.g. constructor:

$EmpObj =
Employee
-
>new
($Name, $SSN, $Salary);




Object

method using object reference $EmpObj:

$NewSalary =
$EmpObj
-
>
SalaryIncrease(0.10);




Opinion
:

There is "indirect syntax" for method calls that I decided to
forget several months ago, e.g. "$db = new Win32::ODBC $DSN;".
My preferred or standard is "$db = Win32::ODBC
-
>new($DSN;".



Reference
:


perlobj

and
perltoot

have excellent discussions.

Sugges
tions
:




If you understand references and modules, you can probably spend your
time with
perltoot

and not purchase a book for Perl O
-
O.



My observation is that books usually provide one chapter on how to write
classes. I had to pull different things from dif
ferent books.


Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
14

of

30

919 / 490
-
0090

Example of Simple Class from perltoot


What caller's code may look like:


use Person; # class name.


$him = Person
-
>new(); # constructor as class method

$him
-
>name("Jason"); # data assessor to set value

$age = $him
-
>age(); #

data assessor to get value

$him
-
>peers( "Norbert", "Rhys", "Phineas" );


What the module would look like to implement this class:


package Person;

use strict;


sub new {


my $Class = shift; # Get class name.


my $Obj = {}; # Set object as hash ref
erence.


$Obj
-
>{NAME} = undef;


$Obj
-
>{AGE} = undef;


$Obj
-
>{PEERS} = [];


bless $Obj, $Class; # Bless hash ref into class.


return $Obj;

}


sub name {


my $Obj = shift; # Get object reference.


if (@_) { $Obj
-
>{NAME} = shift }


return $Obj
-
>{NAME};

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
15

of

30

919 / 490
-
0090

}


sub age {


my $Obj = shift;


if (@_) {
$Obj
-
>{AGE}

= shift }


return
$Obj
-
>{AGE}
;

}


sub peers {


my $Obj = shift;


if (@_) { @{
$Obj
-
>{PEERS}

} = @_ }


return @{
$Obj
-
>{PEERS}

};

}

1; # so the require or use
succeeds


A faster, but less readable implementation of data assessor method is:

sub age {


my $Obj = shift;


return $Obj
-
>{age} = $_[0] if (@_ > 0); #
--

set.


return $Obj
-
>{age}; #
--

get attribute value.

}


Still faster, but even less readable
implementation is:

sub age {


my $Obj = shift;


return (@_ > 0


?

$Obj
-
>{age} # parm to set value.


:

$Obj
-
>{age} # no parm; get value.


);

}

Suggestions
:


Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
16

of

30

919 / 490
-
0090



I prefer $Obj as standard convention, instead of $se
lf as is often used.



Use $Obj
-
>{
age
} for public members, $Obj
-
>{
_age
} for private
members by using the underscore convention.

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
17

of

30

919 / 490
-
0090

How to Represent an Object




Hashes are often used to implement the object, i.e. object reference is a
blessed hash reference
.




Hashes are easy to use for storing public and private members of the
object (also called instance variables). This has worked well in all of my
development.




Nothing requires objects to be implemented as hash references. An object
can be any sort of r
eference so long as its reference has been suitably
blessed.




That means scalar, array, and code references are also fair game.




A scalar would work if the object has only one datum to hold.




An array would work for most cases, but makes inheritance a b
it dodgy
because you have to invent new indices for the derived classes.




A code reference may be too exotic; I have not tried that.



Suggestion
:

Good discussion of this in
perltoot
.

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
18

of

30

919 / 490
-
0090

Categories for Methods




Constructors

such as $EmpObj =
Employee
-
>new
($Name);



Often new() method creates object and initializes member values.



Other times the constructor just creates an empty object.



And uses 2
nd

method to initialize/allocate info for the object.



Depends on your application…




Data assessors

to get/set publ
ic members of the object such as:



$Salary = $EmpObj
-
>Salary(); # to get value



$NewSalary = $EmpObj
-
>Salary($Salary * 1.1); # 10% increase



Sometime data assessor methods are implemented as:



$Salary = $EmpObj
-
>get('Salary'); # to get value



$NewSalary = $
EmpObj
-
>set('Salary', $Salary * 1.1); # to set



Opinion
: I prefer the first style as being more compact. But
main thing is to choose one consistent style to use.




Workhorse

methods

(probably object methods) to perform most of the
functions needed by the ap
plication. Probably where you do most of the
new coding.




Support

methods

such as ErrorMsg(), Debug(), PrintObj() that you tend to
find in many classes. This is where you can inherit common support or
utility methods from a common base class. That is, ut
ility library class.




Destructor

such as
$EmpObj
-
>
DESTROY(); to remove an employee. Note
DESTROY method is automatically called by Perl.

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
19

of

30

919 / 490
-
0090

Expanded Example with Inherited Utility Class


#
-----------------------------------------------------

# MODUL
E: Employee.pm

#

#
-----------------------------------------------------


..... <<Similar to Module POD>> .....


#
-----------

END of Plain Old Documentation (POD)
----

package Employee;

use strict;


#
--

Note no use of Exporter.pm module.


use UtilMethods;

# Utility methods to inherit.


#
--

Must be package globals (not file
-
scoped 'my').

use vars qw(@ISA $VERSION);

@ISA = qw(UtilMethods); #
--

Classes to inherit
.


#
-----------------------------------------------------

# Class Variabl
es

# Some as package global variables for access by

#INHERITED class methods:

use vars qw($DEBUG @_LastErrorListG);

$DEBUG = 0; #
--

Control debug display.

@_LastErrorListG = (); #
--

Save last set of msg.

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
20

of

30

919 / 490
-
0090

#
-------------------------------------
----------------

# Declare 'my' Variables with File Scope Here

#
--

Legal list of user
-
visible attributes

#
--

and default values to build part

#
--

of "instance variables" for each object.

my %
ExternalAttributesG

= (


Attribute1 => 'value',


Attribute2 => 'value',

);

#
--

Internal attributes (not changed by user)

#
--

and default values to build

#
--

part of the "instance varibles" for each object.

my %
InternalAttributesG

= (_Attribute3 => 'value');


=head1 METHODS


=head2 C<new>
--

Constructor Method



my $Obj =
Employee
-
>new
(


Attribute1 => 'aaaaa', #
--

named parameters.


Attribute2 => 'zzzzz', #
--

named parameters.


);



if (not defined $Obj) {


#
--

Class method to call if "new" fails.


pri
nt STDERR Employee
-
>ErrorMsg(), "
\
n";


}


Edit here to describe method.


Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
21

of

30

919 / 490
-
0090

C<Attribute1>
--

Explanation of this named parameter.


=cut

sub new {


my $Class = shift; #
--

class or method call?



$Class = (ref($Class) or $Class or 'Employee');



#
--

Create hash
-
based object w/attr & defaults.


#
--

More attributes added by_AllocateObject.


my $Obj =
$Class>_AllocateObject
(

\
%
ExternalAttributesG
,

\
%
InternalAttributesG
);



bless $Obj, $Class;



$Obj
-
>_PushErrorMsg
(undef); Clear old messag
es.



#
--

Validate all named parameters
. Then replace


#
--

any object's attr values with NAMED parms.


if (not $
Obj
-
>_ReplaceAttributes
(
\
@_, 1)) {


return undef;


}



#
--

Update other attributes for this object.




return $Obj;

} #
--

end of 'new' method.

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
22

of

30

919 / 490
-
0090


=head2 C<age>
--

Data accessor method



$CurrentValue = $Obj
-
>age(); #
--

get attribute.


$UpdatedValue = $Obj
-
>age($NewValue); #
--

set.


For either get or set operation, the value of the
attribute is returned.


=cut

sub a
ge {


my $Obj = shift;



#
--

If another parm present, write attr value.


#
--

Either way, attr value returned.


return $Obj
-
>{age} = $_[0] if (@_ > 0); #
--

write.


return $Obj
-
>{age}; #
--

read attribute.


} #
--

end of get/set age method.


1;

#
--

end of module Employee.pm; modules must return
true.



Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
23

of

30

919 / 490
-
0090

Sample Methods from Utility Class


=head2 C<ErrorMsg>
--

Retrieve list of error messages
previously stored.



@Messages =
$Obj
-
>ErrorMsg(); #
--

list context.


$LastMessage = $Obj
-
>ErrorMs
g(); # scalar context.



@Messages =
$Class
-
>ErrorMsg(); #
--

Class methods


$LastMessage = $Class
-
>ErrorMsg();

....

=cut

sub ErrorMsg {


my $Obj = shift;



#
--

Parameters:


my( $ConcatenateMsgs #
--

Optional boolean.


) = @_;



if (ref($Obj)) {


#
--

Being used as an
object method
.


$ArrayRef = $Obj
-
>{__ErrorMsgList};


} else {


#
--

No object reference;
class method
.


my $Class = $Obj; #
--

Really class!



#
--

Get last set of errors for this c
lass.


no strict 'refs';


$ArrayRef =
\
@{"${Class}::_LastErrorListG"} ;


}

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
24

of

30

919 / 490
-
0090


.....

} #
--

end of ErrorMsg method.



=head2 C<_ReplaceAttributes>
--

Use named parameters
to update object variables

for object.



$OK = $Obj
-
>_ReplaceAttri
butes(
\
@_ );


Validate any named parameter is legal for this class.
If all parameters are valid, replace the value for the
corresponding object variable (attribute). Return
boolean to show valid or invalid parameters. Method
will do error return for any i
nvalid parameters found.


=cut

sub _ReplaceAttributes {


my $Obj = shift;


....


return($OK);

} #
--

end of _ReplaceAttributes method.


=head2 C<SelfDisplay>
--

Display contents of object.


$Obj
-
>SelfDisplay($Title);


$Obj
-
>SelfDisplay(); #
--

suppress title line.


$Obj
-
>SelfDisplay($Title, 1); #
--

compact display.

....

=cut


sub SelfDisplay {


my $Obj = shift;

....

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
25

of

30

919 / 490
-
0090


return;

} #
--

end of SelfDisplay method.

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
26

of

30

919 / 490
-
0090


=head2 C<Debug>
--

Read or write the "debug" setting.



$OldValue = $Obj
-
>
Debug();


$NewValue = $Obj
-
>Debug($Level);



$OldValue = $Class
-
>Debug();


$NewValue = $Class
-
>Debug($Level);


=cut


sub Debug {


my $Obj = shift;


....

} #
--

end of Debug method.


This method will be automatically called when object

goes out of scope or is
undef
-
ed.


sub DESTROY {


my $Obj = shift;


$Obj
-
>DebugMsg("Destroying " . ref($Obj) . " class
for object $Obj...");


undef $Obj;


return;

} #
--

end of DESTROY method




Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
27

of

30

919 / 490
-
0090

AUTOLOADed Data Assessors


=head2 C<Get/Set Attri
bute>
--

Data accessor methods.



$CurrentValue = $Obj
-
>Attribute();


$UpdatedValue = $Obj
-
>Attribute($NewValue);


=cut

sub AUTOLOAD {


my $Obj = shift;


my $Class = ref($Obj)


or confess "DIE:
\
$Obj '$Obj' not object
\
n
\
t";



#
--

$AUTO
LOAD has "Package::Method" to invoke,


#
--

Package may also include '::' separators also.


my $MethodName = $AUTOLOAD;


return if $MethodName =~ m/::DESTROY$/;


$MethodName =~ s/^.+:://; Remove package name.



#
--

Autoload for get/set attri
bute method.


if (
exists $Obj
-
>{__Permitted}
-
>{$MethodName
} ) {


#
--

Autoload get/set for object attribute.


#
--

If another parm, then write attribute.


return $Obj
-
>{$MethodName} = $_[0]

if (@_ > 0); #
--

write attribute.


return
$Obj
-
>{$MethodName}; # read attribute
.


} else {


#
--

Use confess to "die" since this is


#
--
"undefined function" also.


confess "DIE: '$MethodName' method undefined";

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
28

of

30

919 / 490
-
0090


}

} #
--

end of AUTOLOAD method.

Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
29

of

30

919 / 490
-
0090

Candidates to Con
sider for Utility Class


1.

Error message methods
--

Class or object methods. For caller and internal.
And handle list of error messages.


2.

Validate access for public members of object to get/set values. Also
default values for public and internal members of c
less.


3.

Diagnostic methods such as setting flags to enable/disable debugging
code. Also to print out contents of object on requests (See Data::Dumper.)


4.

Display of debug data conditionally based on debug settings.


5.

Support easy use of named parameters with
default values.


6.

Standard initialization of common values to each object, if needed.


Metagenix, Inc.


Leading Way in Data Migration

www.metagenix.com

Page
30

of

30

919 / 490
-
0090

Productivity Thoughts to Code Classes


1.

Use a utility class as a base class in order to inherit common methods
such as error handling or debugging.


2.

Evaluate CPAN tools to

quickly develop classes
--

especially data
assessors
--

such as Class::Struct in perltoot. Class::Struct is useful if you
want to use for object as a data structure. See Class::xxxxx on CPAN.


3.

Develop template or skeleton files for classes and/or methods
to save
keystrokes and encourage consistency.




Also evaluate editor macros and templates.



I am currently playing with Perl template module as tool to generate and
fill
-
in values for methods.


4.


Add __END__ to end of .pm file for the module. You can then p
lace test
cases there and later enable by removing __END__.