The first step – part three
Introduction
Last time in part two we looked at:
- Variables
- Functions
- Scope and globals
This time in part three we will conclude with:
- Classes and methods
- Class inheritance and overloading
If you’ve not read parts one and two, then please do so before this part so that it will make sense.
Disclaimers
Firefox® is a registered trademark of the Mozilla Foundation – www.mozilla.org/en-US/foundation/trademarks/policy/.
Ubuntu® is a registered trademark of Canonical Ltd – ubuntu.com/legal/intellectual-property-policy.
“Raspberry Pi” is a trademark of the Raspberry Pi Foundation – www.raspberrypi.org/trademark-rules.
Apache and Apache HTTP Server are trademarks of The Apache Software Foundation – www.apache.org.
Other names / logos can be trademarks of their respective owners. Please review their website for details.
I am independent from the organisations listed above and am in no way writing for or endorsed by them.
All example ‘code’ presented is written by myself, please feel free to copy for educational purposes only.
A reminder if it does not work
If something goes wrong, then you need to look in the error log file. On the Pi, this will be located in the ‘/var/log/apache2’ folder as ‘error.log’ and you can use the command ‘tail’ to see the most recent events as the file is appended to. In other systems, hunt around for ‘error.log’.
Why ‘Objects’ and ‘Object Orientation’?
In part two we looked at variables, functions and particularly scope. Whilst a machine is happy to perform the commands it is given regardless of the quantity, a human on the other hand understands the greater context of the problem with its solution. They can envisage a concept far beyond that of a single machine instruction or one just about to be in its pipeline. But there is a problem with this, granularity. As we delve deeper into a problem and devise solutions we begin to loose sight of the structure of the solution as a whole. This can lead to similar solutions being used in different parts of the program that do the same thing and can even be coupled to the same data without even realising it, thus causing ‘bugs’ that are difficult to detect and solve. This can be even more obvious when a problem has more than one person working on it in different places.
The solution to this is really a methodology that I think has its origins elsewhere in our technological past. Before computing really took off we built things out of materials we find on this planet, some of them we combine to make other materials as a starting point. Initially lots of people solved the same problem to a similar pattern, but each implementation was not completely compatible with another and when one broke, the fixing it with parts of another would be a hit and miss affair. Then standardisation came along, so think of standard sizes for nuts and bolts as an example. Now solutions were implemented with self contained components that did a specific thing. The components could be tested to a high degree within the functionality they provided and could be moved from one solution to another when breakage occurs.
Now transfer the concept of standardisation to software and you get a methodology whereby you can create self contained components that can contain other components and when lots of them are combined in a planned structure then you have your solution to your problem. You can navigate around the solution at any degree of granularity and each element does the task for which it was designed to solve.
So thus we have the concept of ‘Object Orientation’, please see ‘The ‘OO’ in Moodle’. But as humans are not perfect then there will always be bugs, however we can adapt whereas machines cannot or at least only to situational changes where the solution is already known.
Classes and methods
Think of the class and method as a template that is used to create the actual ‘object’ that will do the work. The class defines what the object is and what data it will store, the methods define what you can do with that object and may change the data it stores.
Both the data and the methods have three levels of access:
- Private – only the object can see / use / change.
- Protected – only the object and any objects that ‘inherit’ from it can see / use / change.
- Public – anything that knows about the object can see / use / change.
So take the code:
<!doctype html> <html> <head> <title>Classes and Methods</title> </head> <body> <?php class Pencil { private $length; private $grade; public function __construct($length = 20, $grade = 'HB') { $this->length = $length; // Centimetres. $this->grade = $grade; } public function sharpen() { $this->length = $this->length - 0.25; } public function write() { $this->length = $this->length - 0.1; } public function getLength() { return $this->length; } public function getGrade() { return $this->grade; } } $pencils = array(); $pencils['1'] = new Pencil(); $pencils['2'] = new Pencil(22, 'B'); $pencils['3'] = new Pencil(18, 'H'); echo '<h1>Initial state</h1>'; foreach ($pencils as $pencilNo => $pencil) { echo "<p>Pencil $pencilNo is of grade: ".$pencil->getGrade().' and length: '.$pencil->getLength().'cm.</p>'; } echo '<h1>Sharpen and use....</h1>'; $pencils['1']->sharpen(); $pencils['2']->write(); $pencils['3']->sharpen(); $pencils['3']->write(); foreach ($pencils as $pencilNo => $pencil) { echo "<p>Pencil $pencilNo is now: ".$pencil->getLength().'cm.</p>'; } ?> </body> </html>
Where we define and use a ‘Pencil’ which has two attributes, its length and its grade (i.e. ‘HB’) which are ‘private’ to it and set when the pencil is created, i.e. instantiated. Once this has been done by using its ‘constructor’ to create the actual ‘object’ then we can only do a certain number of things with it, such as ‘write’. This in the greater picture, prevents data being manipulated in an unintentional way by other code, perhaps written by somebody else that does not know the full details of why the code exists in the first place.
When we run this code by visiting the web page, we get:
Where each pencil is created within an array with different attributes. Then they are used and queried on how long they are.
Class inheritance and overloading
Now that we understand a single class which is a ‘writing implement’, that allows us to see that there are many different writing implements in addition to a pencil, like chalk and a pen that share common traits but are also specialisms of the same thing, so if we have:
<!doctype html> <html> <head> <title>Class inheritance and overloading</title> </head> <body> <?php abstract class writingImplement { protected $length; public function __construct($length) { $this->length = $length; // Centimetres. } public function getLength() { return $this->length; } abstract public function write(); } class Pencil extends writingImplement { private $grade; public function __construct($length = 20, $grade = 'HB') { parent::__construct($length); $this->grade = $grade; } public function sharpen() { $this->length = $this->length - 0.25; } public function write() { $this->length = $this->length - 0.1; } public function getGrade() { return $this->grade; } } class Pen extends writingImplement { private $ink; public function __construct($length = 20, $ink = 50) { parent::__construct($length); $this->ink = $ink; } public function write() { $this->ink = $this->ink - 0.05; } public function getInk() { return $this->ink; } } class Chalk extends writingImplement { public function __construct($length = 20) { parent::__construct($length); } public function write() { $this->length = $this->length - 0.2; } } $writingimplements = array(); $writingimplements['1'] = new Pencil(); $writingimplements['2'] = new Pen(22); $writingimplements['3'] = new Chalk(12); echo '<h1>Initial state</h1>'; foreach ($writingimplements as $writingimplementNo => $writingimplement) { echo "<p>Writing implement $writingimplementNo has a length of: ".$writingimplement->getLength().'cm and is '; switch (get_class($writingimplement)) { case 'Pencil': echo 'a pencil of grade '.$writingimplement->getGrade(); break; case 'Pen': echo 'a pen with an amount of ink of '.$writingimplement->getInk(); break; case 'Chalk': echo 'chalk'; break; } echo '.</p>'; } echo '<h1>Use....</h1>'; $writingimplements['1']->write(); $writingimplements['2']->write(); $writingimplements['3']->write(); foreach ($writingimplements as $writingimplementNo => $writingimplement) { echo "<p>Writing implement $writingimplementNo has a length of: ".$writingimplement->getLength().'cm and is '; switch (get_class($writingimplement)) { case 'Pencil': echo 'a pencil'; break; case 'Pen': echo 'a pen with an amount of ink of '.$writingimplement->getInk(); break; case 'Chalk': echo 'chalk'; break; } echo '.</p>'; } ?> </body> </html>
Then we can see that we have an ‘abstract’ class called ‘writingImplement’ that cannot be made into an object but has the common attribute of ‘length’ and all can ‘write’ but that ‘method’ is abstract because the effect on the implement is different. So now, Pencil, Pen and Chalk can ‘inherit’ from ‘writingImplement’ without having to reimplement the ‘length’ functionality. Now the output is:
Where we can see the objects being created with the common length attribute along with their own specialised attribute. Then when used the effect of the ‘write’ method has a different outcome.
Further reading
To learn more about PHP, you could choose to visit:
Conclusion
In this post we have learnt about:
- Classes and methods
- Class inheritance and overloading
I hope you’ve progressed with your learning of PHP, this is the last part for now.
- Added value – 16th September 2024
- What is a developer? – 16th August 2024
- A little boost – 16th July 2024
Pingback: What a difference a character makes - ElearningWorld.org
Great analogies used here Gareth – really helped my understanding of these concepts !