.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Moops::Manual::Objects101 3pm" .TH Moops::Manual::Objects101 3pm "2020-08-09" "perl v5.30.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Moops::Manual::Objects101 \- an introduction to object oriented programming using Moops .SH "SYNOPSIS" .IX Header "SYNOPSIS" This tutorial assumes some knowledge of procedural programming in Perl, and provides a gentle introduction to the object oriented programming paradigm. .SH "DESCRIPTION" .IX Header "DESCRIPTION" Object oriented programming is a programming paradigm based around programming \fIclasses\fR, and manipulating instances of those classes called \fIobjects\fR. These can be objects that appear on the screen (e.g., pictures, textboxes, etc.) or are part of the programming (e.g. actors, connections, particles, etc.). .SS "Classes" .IX Subsection "Classes" \&\fIStructures\fR are very similar to \fIclasses\fR in that they collect data together. However, classes extend this idea and are made from two different things: .IP "\fIAttributes\fR" 4 .IX Item "Attributes" Things that the object stores data in, similar to variables. .IP "\fIMethods\fR" 4 .IX Item "Methods" Functions and Procedures attached to an Object and allowing the object to perform actions. .PP Let's take a look at the following example: .PP .Vb 1 \& use Moops; \& \& class Car \& { \& lexical_has max_speed => ( \& is => \*(Aqrw\*(Aq, \& accessor => \e(my $max_speed), \& isa => Int, \& default => 90, \& ); \& \& has fuel => ( \& is => \*(Aqrw\*(Aq, \& isa => Int, \& ); \& \& has speed => ( \& is => \*(Aqrw\*(Aq, \& isa => Int, \& trigger => method ($new, $old?) { \& confess "Cannot travel at a speed of $new; too fast" \& if $new > $self\->$max_speed; \& }, \& ); \& \& method refuel (Int $x) { \& say "Pumping gas!"; \& $self\->fuel( $self\->fuel + $x ); \& } \& \& method drive () { \& $self\->fuel( $self\->fuel \- 1 ); \& } \& } .Ve .PP You can see that the class is called Car and it has: .IP "\(bu" 4 Three attributes: \f(CW$max_speed\fR (which is a lexical/private attribute), \&\f(CW\*(C`fuel\*(C'\fR, and \f(CW\*(C`speed\*(C'\fR; .IP "\(bu" 4 Two methods: \f(CW\*(C`refuel\*(C'\fR and \f(CW\*(C`drive\*(C'\fR. .PP Remember this is a class and therefore only a template, we need to \*(L"create\*(R" it using an object. .PP \fIAttributes\fR .IX Subsection "Attributes" .PP These store information about the object. In the example above we store the \f(CW\*(C`fuel\*(C'\fR, \f(CW\*(C`speed\*(C'\fR, and \f(CW$max_speed\fR. The attributes are attached to the class, and if there are several instances (objects) of the class then each will store its own version of these variables. .PP Unlike many other programming languages, Perl has no native concept of attributes. The \f(CW\*(C`has\*(C'\fR keyword instead sets up some extra methods for your class, called \*(L"accessors\*(R", which allow you to access the data for the attributes. .PP \&\f(CW$max_speed\fR has a lexical accessor, stored in a lexical (\f(CW\*(C`my\*(C'\fR) variable, so cannot be directly accessed outside the scope where it was declared. .PP \fIMethods\fR .IX Subsection "Methods" .PP Unlike structures, \s-1OOP\s0 allows you to attach functions and procedures to your code. This means that not only can you store details about you car (the attributes), you can also allow for subroutines such as \f(CW\*(C`drive\*(C'\fR and \f(CW\*(C`refuel\*(C'\fR, which are attached to each class. .SS "\s-1OO\s0 \- \s-1PIIE\s0" .IX Subsection "OO - PIIE" When talking about \s-1OOP\s0 you must remember \fIOO-PIIE\fR (yummy pie!) .IP "\fB\s-1OO\s0\fR" 4 .IX Item "OO" Object Orientation .IP "\fB\s-1PIIE\s0\fR" 4 .IX Item "PIIE" Polymorphism, Inheritance, Instantiation, and Encapsulation .PP \fIInstantiation\fR .IX Subsection "Instantiation" .PP As we have seen a class is a template for something; you can't actually execute a class; you must instantiate it: that is create an instance of an class in the form of an object. .PP .Vb 2 \& my $polo = Car\->new; \& my $escort = Car\->new; .Ve .PP The code above creates objects called \f(CW$polo\fR and \f(CW$escort\fR, both of class type Car (which we declared earlier). We can now use all the public attributes and methods: .PP .Vb 3 \& $polo\->refuel(100); # assuming fuel starts at 0 \& $polo\->drive(); \& $polo\->drive(); \& \& $escort\->refuel(50); # assuming fuel starts at 0 \& \& for my $x (1 .. 20) { \& $escort\->drive(); \& $polo\->drive(); \& } \& \& $polo\->refuel(10); \& \& say "polo: ", $polo\->fuel; \& say "escort: ", $escort\->fuel; .Ve .PP This would output the following: .PP .Vb 5 \& Pumping gas! \& Pumping gas! \& Pumping gas! \& polo: 88 \& escort: 30 .Ve .PP \fIEncapsulation\fR .IX Subsection "Encapsulation" .PP You noticed that we used \f(CW\*(C`lexical_has\*(C'\fR to declare \f(CW\*(C`max_speed\*(C'\fR instead of \&\f(CW\*(C`has\*(C'\fR. What this means is that these attributes are not directly accessible from outside the definition of the class. Let's take our polo class as an example: .PP .Vb 1 \& $polo\->fuel(100); # this directly alters the fuel level of $polo .Ve .PP In the example we access the \f(CW\*(C`fuel\*(C'\fR attribute of the polo class and give the car 100 units of fuel. Because \f(CW\*(C`fuel\*(C'\fR is declared as public, there are no restrictions in accessing it. .PP However, when we try the following we run into trouble: .PP .Vb 1 \& $polo\->$max_speed(100); .Ve .PP The reason that this wouldn't work is because we have declared the \&\f(CW$max_speed\fR attribute lexically. If something is declared lexically you can't access it externally, but how do you access it? The only way to access a lexical (private) attribute is to use a public method. .PP In the Car code example we could have: .PP .Vb 3 \& method get_top_speed () { \& return $self\->$max_speed; \& } .Ve .PP Because this method is public we can call it from outside the class. And because \f(CW\*(C`get_top_speed\*(C'\fR is declared inside the Car class, it can have access to all the private attributes and methods. .PP \fIInheritance\fR .IX Subsection "Inheritance" .PP Building on the car example above, what would happen if we wanted to declare an electric car? Well we'd probably want to store some information on the number of batteries that it has: .PP .Vb 1 \& use Moops imports => [\*(AqLexical::Accessor\*(Aq]; \& \& class ElectricCar \& { \& lexical_has max_speed => ( \& is => \*(Aqrw\*(Aq, \& accessor => \e(my $max_speed), \& isa => Int, \& default => 90, \& ); \& \& has num_batteries => ( \& is => \*(Aqrw\*(Aq, \& isa => Int, \& default => 8, \& ); \& \& has fuel => ( \& is => \*(Aqrw\*(Aq, \& isa => Int, \& ); \& \& has speed => ( \& is => \*(Aqrw\*(Aq, \& isa => Int, \& trigger => method ($new, $old?) { \& confess "Cannot travel at a speed of $new; too fast" \& if $new > $self\->$max_speed; \& }, \& ); \& \& method refuel (Int $x) { \& # HOLD ON! \& } \& } .Ve .PP This seems like a very long and tedious task rewriting all the same code again. You're right! It would be far better if we only had to declare all the new stuff we wanted to add. \s-1OOP\s0 allows for inheritance, where a new class can inherit the attributes and methods of a parent class: .PP .Vb 7 \& class ElectricCar extends Car { \& has num_batteries => ( \& is => \*(Aqro\*(Aq, \& isa => Int, \& default => 8, \& ); \& } .Ve .PP This means that everything that \f(CW\*(C`Car\*(C'\fR declared is now accessible from \&\f(CW\*(C`ElectricCar\*(C'\fR, as well as the new \f(CW\*(C`num_batteries\*(C'\fR attribute. Let's instantiate this example and see what's possible: .PP .Vb 3 \& my $gwiz = ElectricCar\->new(num_batteries => 6, fuel => 100); \& say $gwiz\->num_batteries; # defined in ElectricCar \& say $gwiz\->fuel; # defined in Car .Ve .PP Using inheritance makes creating new classes very quick and easy. It also allows for a modular approach to creating classes, where you might never use the base class at all, but only as a means of creating other child classes. .PP Rather than having to rewrite the same functionality for similar objects, \&\s-1OOP\s0 allows you to reuse attributes and methods from parent classes. .PP \fIPolymorphism\fR .IX Subsection "Polymorphism" .PP When you were young you might have watched the \fIMighty Morphin' Power Rangers\fR. These guys could morph from regular people into their power suits. Sadly polymorphism isn't that exciting, but it does allow you to change the function of attributes and methods when you are inheriting from a parent class. Consider our car example again. When we created the \&\f(CW\*(C`ElectricCar\*(C'\fR object we inherited from \f(CW\*(C`Car\*(C'\fR and added the \f(CW\*(C`num_batteries\*(C'\fR attribute and methods. But what happens when we try and refuel, let's take a look at the code: .PP .Vb 4 \& method refuel (Int $x) { \& say "Pumping gas!"; \& $self\->fuel( $self\->fuel + $x ); \& } .Ve .PP Well this just won't do! We are creating an electric car and we don't want to say that we are pumping gas; what would our sandal-socked yoghurt eating friends say?! So for the \f(CW\*(C`ElectricCar\*(C'\fR class, we want to inherit everything from \f(CW\*(C`Car\*(C'\fR, but we want to morph(change) the \f(CW\*(C`refuel\*(C'\fR method. To do that we \fIoverride\fR the method inherited from the superclass: .PP .Vb 11 \& class ElectricCar extends Car { \& has num_batteries => ( \& is => \*(Aqro\*(Aq, \& isa => Int, \& default => 8, \& ); \& override refuel (Int $x) is override { \& say "Pure renewable energy!"; \& $self\->fuel( $self\->fuel + $x ); \& } \& } .Ve .SH "BUGS" .IX Header "BUGS" Please report any other bugs to . .SH "SEE ALSO" .IX Header "SEE ALSO" Moops. .SH "AUTHOR" .IX Header "AUTHOR" Toby Inkster , plus the authors credited at: (as of 15 December 2013). .SH "COPYRIGHT AND LICENCE" .IX Header "COPYRIGHT AND LICENCE" Copyright 2013\-2014 Toby Inkster and others. .PP Text is available under the Creative Commons Attribution/Share\-Alike License. .PP . .SH "DISCLAIMER OF WARRANTIES" .IX Header "DISCLAIMER OF WARRANTIES" \&\s-1THIS PACKAGE IS PROVIDED \*(L"AS IS\*(R" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.\s0