Classes and OOP (Part 5)

  • Bogey
  • Genius
  • Genius
  • Bogey
  • Posts: 8399
  • Loc: USA

Post 3+ Months Ago

Classes and OOP (Part 5)



1.0 Previous Tutorial


This is the fifth tutorial in a series of tutorials on classes and OOP.

First Tutorial
Second Tutorial
Third Tutorial
Fourth Tutorial

In the previous tutorial we learned about interfaces, their use in the programming world and multiple different ways we could use them (such as extending one or multiple interfaces or implementing 1 or more interfaces).

1.1 Tutorial Introduction


In this tutorial we would dwell into something similar to interfaces but not exactly. What I'm talking about is abstract classes and functions. In my view, abstract classes are more advanced.

By definition, the word 'abstract' means "existing in thought or as an idea but not having a physical or concrete existence." (Source: Google).

That is what we had in interfaces... those named methods (without a body) are abstract, because they exist, but don't have a concrete physical existence. Just a name.
PHP Code: [ Select ]
function abstract_function();


We are not going to use the file we were finished off with in the previous tutorial, but we are going to use the file we were left off with in the third tutorial of this series.
PHP Code: [ Select ]
<?php
class fruit
{
 
    private $amount = 2;
   
    public function __construct()
    {
        echo "Picking up a(n) {$this->fruit}.<br />\n";
    }
 
    public function eat()
    {
        $this->prepare();
        $this->chew();
        $this->swallow();
    }
   
    public function amount($number)
    {
        if($number < 11)
        {
            $this->amount = $number;
        }
    }
   
    public function __destruct()
    {
        echo "We ate a total of {$this->amount} {$this->fruit}(s).<br />\n";
    }
   
}
 
class apple extends fruit
{
 
    protected $fruit = 'apple';
 
    protected function prepare()
    {
        echo "Washing the apple.<br />";
    }
   
    protected function chew()
    {
        echo "Biting the apple.<br />";
        echo "Chewing the apple.<br />";
    }
   
    protected function swallow()
    {
        echo "Swallowing the apple.<br />";
    }
 
}
 
class banana extends fruit
{
 
    protected $fruit = 'banana';
 
    protected function prepare()
    {
        echo "Peeling the banana.<br />";
    }
   
    protected function chew()
    {
        echo "Biting the banana.<br />";
        echo "Chewing the banana.<br />";
    }
   
    protected function swallow()
    {
        echo "Swallowing the banana.<br />";
    }
 
}
 
$fruit = 'banana';
 
$product = new $fruit();
 
$product->eat();
?>
  1. <?php
  2. class fruit
  3. {
  4.  
  5.     private $amount = 2;
  6.    
  7.     public function __construct()
  8.     {
  9.         echo "Picking up a(n) {$this->fruit}.<br />\n";
  10.     }
  11.  
  12.     public function eat()
  13.     {
  14.         $this->prepare();
  15.         $this->chew();
  16.         $this->swallow();
  17.     }
  18.    
  19.     public function amount($number)
  20.     {
  21.         if($number < 11)
  22.         {
  23.             $this->amount = $number;
  24.         }
  25.     }
  26.    
  27.     public function __destruct()
  28.     {
  29.         echo "We ate a total of {$this->amount} {$this->fruit}(s).<br />\n";
  30.     }
  31.    
  32. }
  33.  
  34. class apple extends fruit
  35. {
  36.  
  37.     protected $fruit = 'apple';
  38.  
  39.     protected function prepare()
  40.     {
  41.         echo "Washing the apple.<br />";
  42.     }
  43.    
  44.     protected function chew()
  45.     {
  46.         echo "Biting the apple.<br />";
  47.         echo "Chewing the apple.<br />";
  48.     }
  49.    
  50.     protected function swallow()
  51.     {
  52.         echo "Swallowing the apple.<br />";
  53.     }
  54.  
  55. }
  56.  
  57. class banana extends fruit
  58. {
  59.  
  60.     protected $fruit = 'banana';
  61.  
  62.     protected function prepare()
  63.     {
  64.         echo "Peeling the banana.<br />";
  65.     }
  66.    
  67.     protected function chew()
  68.     {
  69.         echo "Biting the banana.<br />";
  70.         echo "Chewing the banana.<br />";
  71.     }
  72.    
  73.     protected function swallow()
  74.     {
  75.         echo "Swallowing the banana.<br />";
  76.     }
  77.  
  78. }
  79.  
  80. $fruit = 'banana';
  81.  
  82. $product = new $fruit();
  83.  
  84. $product->eat();
  85. ?>


2.0 Abstract Classes


An abstract class is a sort of an advanced interface. It's purpose is the same as interfaces and it adds more functionality and provides for more uses then interfaces.

Abstract classes lay out the outline for how other child classes extending it should look like. Just like an interface, all of the classes named in an abstract class has to be present in the child class extending it with the body for the class. In essence, it's like fill in the blanks... the abstract class provides the idea of the class while the extending child classes fills in the blanks.

On top of having named functions in an abstract class, you could also have a full function with body and everything. If you have a fully named and defined function in an abstract class you don't put it in the child class extending the abstract class. You also (if you need to) may over-ride functions in a child class that you have named and defined in an abstract class.

To make the class abstract, you use the keyword abstract before the word 'class' when you start writing the class (e.g: abstract class fruit). In an abstract class, all the functions that are just named (without the body), needs to have the keyword abstract in front of the function (e.g: abstract function prepare()).

Unlike an interface where you could only have public methods, in an abstract class you could also have protected methods as well. You still can't have a private method in an abstract class because a private method means that no other class can touch it, but in this case, there would be a child class that is forced to have a function named like that, so the strictest access level you can use in an abstract class is protected.

You can have private functions/members in an abstract class if you only use it in the abstract class and not in the child class extending it. For instance the variable $amount in our abstract class we are only using within the abstract class (function 'amount()' is available for us to use without defining it in the child class). Same thing with functions, we could have them private only if we are using them in the abstract class and not in the child class.

We need to add the keyword abstract to the class 'fruit' that we are using (provided at the top). So now our 'fruit' class looks like:
PHP Code: [ Select ]
abstract class fruit
{
 
    private $amount = 2;
   
    public function __construct()
    {
        echo "Picking up a(n) {$this->fruit}.<br />\n";
    }
 
    public function eat()
    {
        $this->prepare();
        $this->chew();
        $this->swallow();
    }
   
    public function amount($number)
    {
        if($number < 11)
        {
            $this->amount = $number;
        }
    }
   
    public function __destruct()
    {
        echo "We ate a total of {$this->amount} {$this->fruit}(s).<br />\n";
    }
   
}
  1. abstract class fruit
  2. {
  3.  
  4.     private $amount = 2;
  5.    
  6.     public function __construct()
  7.     {
  8.         echo "Picking up a(n) {$this->fruit}.<br />\n";
  9.     }
  10.  
  11.     public function eat()
  12.     {
  13.         $this->prepare();
  14.         $this->chew();
  15.         $this->swallow();
  16.     }
  17.    
  18.     public function amount($number)
  19.     {
  20.         if($number < 11)
  21.         {
  22.             $this->amount = $number;
  23.         }
  24.     }
  25.    
  26.     public function __destruct()
  27.     {
  28.         echo "We ate a total of {$this->amount} {$this->fruit}(s).<br />\n";
  29.     }
  30.    
  31. }

There's that keyword abstract that we are using to tell the PHP engine/parser that the class is abstract. If you actually try to run the script with that class, it will work, but we are not done here.

We are using 3 functions in the child classes that the abstract class is not enforcing us to use (besides in the 'eat()' function). Those functions are 'prepare()', 'chew()' and 'swallow()'. Let's add those to the class after the function 'amount()'.
PHP Code: [ Select ]
    protected abstract function prepare();
   
    protected abstract function chew();
   
    protected abstract function swallow();
  1.     protected abstract function prepare();
  2.    
  3.     protected abstract function chew();
  4.    
  5.     protected abstract function swallow();

I want to point out here that it doesn't matter in what order you put the keywords in. You could have just as easily done 'abstract protected function prepare()' instead of 'protected abstract function prepare()' and it would work just the same.

Now what we have done is named and defined all of the functions in the abstract class 'fruit' that would be used in all the child classes (or relevant to all of the child classes), and just named all of the functions that the child classes needed to define.

We also made three functions 'protected', meaning that they can not be touched outside of the class, bringing the enforcement of our ordering into play here (as we had in our third tutorial).

If you define an abstract member of an abstract class protected, the same member in the child class must be defined in the same or less restrictive access level. This means if our method 'prepare()' is made 'protected' in the abstract class, when we define it in the child class, we could name it as either 'protected' or 'public', but not 'private'.

If you don't define an access level in the abstract class, then it is assumed that the method is public, and in the child class, you would only be able to define those methods as public.

2.1 Multiple Abstract Classes


It is possible for an abstract class to extend an abstract class (just like an interface extending an interface). Unlike interfaces though, you can't extend more then one abstract class (means no delimited lists :( ). For example, the following abstract class.
PHP Code: [ Select ]
abstract class food
{
 
    protected abstract function prepare();
   
    protected abstract function chew();
   
    protected abstract function swallow();
   
}
 
abstract class fruit extends food
{
 
    private $amount = 2;
   
    public function __construct()
    {
        echo "Picking up a(n) {$this->fruit}.<br />\n";
    }
 
    public function eat()
    {
        $this->prepare();
        $this->chew();
        $this->swallow();
    }
   
    public function amount($number)
    {
        if($number < 11)
        {
            $this->amount = $number;
        }
    }
   
    public function __destruct()
    {
        echo "We ate a total of {$this->amount} {$this->fruit}(s).<br />\n";
    }
 
}
  1. abstract class food
  2. {
  3.  
  4.     protected abstract function prepare();
  5.    
  6.     protected abstract function chew();
  7.    
  8.     protected abstract function swallow();
  9.    
  10. }
  11.  
  12. abstract class fruit extends food
  13. {
  14.  
  15.     private $amount = 2;
  16.    
  17.     public function __construct()
  18.     {
  19.         echo "Picking up a(n) {$this->fruit}.<br />\n";
  20.     }
  21.  
  22.     public function eat()
  23.     {
  24.         $this->prepare();
  25.         $this->chew();
  26.         $this->swallow();
  27.     }
  28.    
  29.     public function amount($number)
  30.     {
  31.         if($number < 11)
  32.         {
  33.             $this->amount = $number;
  34.         }
  35.     }
  36.    
  37.     public function __destruct()
  38.     {
  39.         echo "We ate a total of {$this->amount} {$this->fruit}(s).<br />\n";
  40.     }
  41.  
  42. }

You also can't extend more then one abstract class with a child like you could with an interface.

2.2 Static Context


If you remember previously we were talking about writing methods in a static context and how to use such classes. It is also (as a reminder) bad practice (in most cases) as it puts the static members in a global scale, voiding OOP encapsulation.

What I want to point out in this section is that you could (if you need to) have static members in an abstract class, but only if that member is defined (has a body, and not just named [just the signature]). For example, the following code has two members... one that could be static and one that can't. The one member that can be static has the keyword static in front of it, and the one that can't, doesn't. (This code is not part of our tutorial file).
PHP Code: [ Select ]
abstract class car
{
 
    // This is just a signature (a named function)
    abstract function make();
   
    // This is a fully defined function
    abstract static function drive()
    {  
        // This function has a body
        echo "Vrooooom!";
    }
}
  1. abstract class car
  2. {
  3.  
  4.     // This is just a signature (a named function)
  5.     abstract function make();
  6.    
  7.     // This is a fully defined function
  8.     abstract static function drive()
  9.     {  
  10.         // This function has a body
  11.         echo "Vrooooom!";
  12.     }
  13. }


2.3 Instantiation


I want to point out here that just like interfaces, you can't instantiate an abstract class like you would be able with any other class. For example, the following code would fail because the class 'fruit' is an abstract class.
PHP Code: [ Select ]
<?php
$product = new fruit();
 
$product->eat();
?>
  1. <?php
  2. $product = new fruit();
  3.  
  4. $product->eat();
  5. ?>

This is another powerful thing about abstract classes (and interfaces). For instance, if we had a function 'taste()' which we would call to see how an apple or a banana tastes, it wouldn't make any sense if we ask the class 'fruit' on how a 'fruit' tastes. It would make sense if we ask a class 'apple' (which is extending the abstract class 'fruit') on how an 'apple' tastes. The same with the class 'banana'.

2.4 Inheritance and Hierarchy


If you remember from the third tutorial where the parent not being able to inherit the members of a child class or a grandparent not being able to inherit the members of a parent and child class (the parent class being a child class to the grandparent class), that changes a little bit here. (If you haven't noticed that already).

If you already have noticed that in the abstract class 'fruit' we have a function named 'eat()' and in that function, we are calling on three functions defined in the child classes. In this case, the parent does inherit the child's members and can use them in it's own functions.

It's also true if you are extending an abstract class with another abstract class. The parent abstract class inherits the abstract child's members and can use those members in it's own functions. For instance, if we change our abstract class 'food' and 'fruit' to the following, it would still work fine.
PHP Code: [ Select ]
abstract class food
{
 
    protected abstract function prepare();
   
    protected abstract function chew();
   
    protected abstract function swallow();
   
    public function eat()
    {
        $this->devour();
    }
   
}
 
abstract class fruit extends food
{
 
    private $amount = 2;
   
    public function __construct()
    {
        echo "Picking up a(n) {$this->fruit}.<br />\n";
    }
 
    protected function devour()
    {
        $this->prepare();
        $this->chew();
        $this->swallow();
    }
   
    public function amount($number)
    {
        if($number < 11)
        {
            $this->amount = $number;
        }
    }
   
    public function __destruct()
    {
        echo "We ate a total of {$this->amount} {$this->fruit}(s).<br />\n";
    }
 
}
  1. abstract class food
  2. {
  3.  
  4.     protected abstract function prepare();
  5.    
  6.     protected abstract function chew();
  7.    
  8.     protected abstract function swallow();
  9.    
  10.     public function eat()
  11.     {
  12.         $this->devour();
  13.     }
  14.    
  15. }
  16.  
  17. abstract class fruit extends food
  18. {
  19.  
  20.     private $amount = 2;
  21.    
  22.     public function __construct()
  23.     {
  24.         echo "Picking up a(n) {$this->fruit}.<br />\n";
  25.     }
  26.  
  27.     protected function devour()
  28.     {
  29.         $this->prepare();
  30.         $this->chew();
  31.         $this->swallow();
  32.     }
  33.    
  34.     public function amount($number)
  35.     {
  36.         if($number < 11)
  37.         {
  38.             $this->amount = $number;
  39.         }
  40.     }
  41.    
  42.     public function __destruct()
  43.     {
  44.         echo "We ate a total of {$this->amount} {$this->fruit}(s).<br />\n";
  45.     }
  46.  
  47. }


3.0 Abstract VS Interface


While it may seem like interfaces and abstract classes or similar, there are differences between them. Below are some key differences between an abstract class and an interface (Source: StackOverflow).
  • Abstract classes can have constants, members, method stubs and defined methods, whereas interfaces can only have constants and methods stubs.
  • Methods and members of an abstract class can be defined with any visibility, whereas all methods of an interface must be defined as public.
  • When inheriting an abstract class, a concrete child class must define the abstract methods, whereas an an abstract class can extend another abstract class and abstract methods from the parent class don't have to be defined.
  • Similarly, an interface extending another interface is not responsible for implementing methods from the parent interface. This is because interfaces cannot define any implementation.
  • A child class can only extend a single abstract (or any other) class, whereas an interface can extend or a class can implement multiple other interfaces.
  • A child class can define abstract methods with the same or less restrictive visibility, whereas a class implementing an interface must define the methods with the exact same visibility.

Mainly, an interface doesn't care how you come up with the public methods (the methods that would be accessed outside of the class). It just cares about the public methods (the signatures in the interface/named functions).

An abstract class is the same, with the addition of sharing functions with child classes. Since in some systems, different modules may have certain similarity (for instance a database access wrapper), the abstract class would contain the function signatures (mostly the public methods used to access the functions) like an interface would, plus contain the similarities.

Those similarities might be protected (not accessible outside of class) since they are specific for the class/function and shouldn't be messed with outside of class.

For example we have a database access wrapper. There are different kinds of databases, such as MySQL and SQLite (there are others, but for the sake of simplicity, our example contains two). We would use an abstract class named 'database' and then have a child class 'mysql' and 'sqlite' which would be extending the abstract class 'database'.

Both of those databases/servers would have public methods connect, query and close (plus others, but for the sake of simplicity of the example, we'll only have these three), so the abstract class would only have a signature for those functions... it would just name them. Both of those servers would need to sanitize the input and they would be done the same way.

So the function 'sanitize()' would be defined in the abstract class and used in the child 'query()' function to sanitize the input that is to be queries.

3.1 Abstracts Implementing Interfaces


There is also such a thing when an abstract class implements an interface. This is also useful if you have different abstract classes that would (should) be used the same way.

For instance, an interface would be 'food' and an abstract class implementing would be 'fruit' with the class 'banana' extending the abstract class fruit. Then we have an abstract class 'dessert' implementing the interface 'food' with the class 'iceCream' extending the abstract class 'dessert'. You would have the functions 'prepare()', 'chew()', 'swallow()' and 'eat()' for all of them since you do that to everything that is food, but the way you go about doing it would be different.

For example (not part of the tutorial file, but you could run it if you want to):
PHP Code: [ Select ]
<?php
interface food
{
 
    function eat();
 
}
 
abstract class fruit implements food
{
 
    private $amount = 2;
   
    public function __construct()
    {
        echo "Picking up a(n) {$this->fruit}.<br />\n";
    }
 
    public function eat()
    {
        $this->prepare();
        $this->chew();
        $this->swallow();
    }
   
    public function amount($number)
    {
        if($number < 11)
        {
            $this->amount = $number;
        }
    }
   
    public function __destruct()
    {
        echo "We ate a total of {$this->amount} {$this->fruit}(s).<br />\n";
    }
 
}
 
class apple extends fruit
{
 
    protected $fruit = 'apple';
 
    protected function prepare()
    {
        echo "Washing the apple.<br />";
    }
   
    protected function chew()
    {
        echo "Biting the apple.<br />";
        echo "Chewing the apple.<br />";
    }
   
    protected function swallow()
    {
        echo "Swallowing the apple.<br />";
    }
 
}
 
$product = new apple();
 
$product->eat();
?>
  1. <?php
  2. interface food
  3. {
  4.  
  5.     function eat();
  6.  
  7. }
  8.  
  9. abstract class fruit implements food
  10. {
  11.  
  12.     private $amount = 2;
  13.    
  14.     public function __construct()
  15.     {
  16.         echo "Picking up a(n) {$this->fruit}.<br />\n";
  17.     }
  18.  
  19.     public function eat()
  20.     {
  21.         $this->prepare();
  22.         $this->chew();
  23.         $this->swallow();
  24.     }
  25.    
  26.     public function amount($number)
  27.     {
  28.         if($number < 11)
  29.         {
  30.             $this->amount = $number;
  31.         }
  32.     }
  33.    
  34.     public function __destruct()
  35.     {
  36.         echo "We ate a total of {$this->amount} {$this->fruit}(s).<br />\n";
  37.     }
  38.  
  39. }
  40.  
  41. class apple extends fruit
  42. {
  43.  
  44.     protected $fruit = 'apple';
  45.  
  46.     protected function prepare()
  47.     {
  48.         echo "Washing the apple.<br />";
  49.     }
  50.    
  51.     protected function chew()
  52.     {
  53.         echo "Biting the apple.<br />";
  54.         echo "Chewing the apple.<br />";
  55.     }
  56.    
  57.     protected function swallow()
  58.     {
  59.         echo "Swallowing the apple.<br />";
  60.     }
  61.  
  62. }
  63.  
  64. $product = new apple();
  65.  
  66. $product->eat();
  67. ?>

You would notice that in our interface we only have one function defined 'eat()'. That is because all members of the interface must be public, otherwise it's not an interface any more. The interface is the middleman between the class and the user, so all members defined needs to be public so the user can access them.

If you ever need to or want to, you could implement more than one interface by making a comma delimited list after the word 'implements':
PHP Code: [ Select ]
abstract class fruit implements food, product


4.0 Our File


Our file at the end of this tutorial should look like:
PHP Code: [ Select ]
<?php
abstract class food
{
 
    protected abstract function prepare();
   
    protected abstract function chew();
   
    protected abstract function swallow();
   
    public function eat()
    {
        $this->devour();
    }
   
}
 
abstract class fruit extends food
{
 
    private $amount = 2;
   
    public function __construct()
    {
        echo "Picking up a(n) {$this->fruit}.<br />\n";
    }
 
    protected function devour()
    {
        $this->prepare();
        $this->chew();
        $this->swallow();
    }
   
    public function amount($number)
    {
        if($number < 11)
        {
            $this->amount = $number;
        }
    }
   
    public function __destruct()
    {
        echo "We ate a total of {$this->amount} {$this->fruit}(s).<br />\n";
    }
 
}
 
class apple extends fruit
{
 
    protected $fruit = 'apple';
 
    protected function prepare()
    {
        echo "Washing the apple.<br />";
    }
   
    protected function chew()
    {
        echo "Biting the apple.<br />";
        echo "Chewing the apple.<br />";
    }
   
    protected function swallow()
    {
        echo "Swallowing the apple.<br />";
    }
 
}
 
class banana extends fruit
{
 
    protected $fruit = 'banana';
 
    protected function prepare()
    {
        echo "Peeling the banana.<br />";
    }
   
    protected function chew()
    {
        echo "Biting the banana.<br />";
        echo "Chewing the banana.<br />";
    }
   
    protected function swallow()
    {
        echo "Swallowing the banana.<br />";
    }
 
}
$fruit = 'apple';
 
$product = new $fruit();
 
$product->eat();
?>
  1. <?php
  2. abstract class food
  3. {
  4.  
  5.     protected abstract function prepare();
  6.    
  7.     protected abstract function chew();
  8.    
  9.     protected abstract function swallow();
  10.    
  11.     public function eat()
  12.     {
  13.         $this->devour();
  14.     }
  15.    
  16. }
  17.  
  18. abstract class fruit extends food
  19. {
  20.  
  21.     private $amount = 2;
  22.    
  23.     public function __construct()
  24.     {
  25.         echo "Picking up a(n) {$this->fruit}.<br />\n";
  26.     }
  27.  
  28.     protected function devour()
  29.     {
  30.         $this->prepare();
  31.         $this->chew();
  32.         $this->swallow();
  33.     }
  34.    
  35.     public function amount($number)
  36.     {
  37.         if($number < 11)
  38.         {
  39.             $this->amount = $number;
  40.         }
  41.     }
  42.    
  43.     public function __destruct()
  44.     {
  45.         echo "We ate a total of {$this->amount} {$this->fruit}(s).<br />\n";
  46.     }
  47.  
  48. }
  49.  
  50. class apple extends fruit
  51. {
  52.  
  53.     protected $fruit = 'apple';
  54.  
  55.     protected function prepare()
  56.     {
  57.         echo "Washing the apple.<br />";
  58.     }
  59.    
  60.     protected function chew()
  61.     {
  62.         echo "Biting the apple.<br />";
  63.         echo "Chewing the apple.<br />";
  64.     }
  65.    
  66.     protected function swallow()
  67.     {
  68.         echo "Swallowing the apple.<br />";
  69.     }
  70.  
  71. }
  72.  
  73. class banana extends fruit
  74. {
  75.  
  76.     protected $fruit = 'banana';
  77.  
  78.     protected function prepare()
  79.     {
  80.         echo "Peeling the banana.<br />";
  81.     }
  82.    
  83.     protected function chew()
  84.     {
  85.         echo "Biting the banana.<br />";
  86.         echo "Chewing the banana.<br />";
  87.     }
  88.    
  89.     protected function swallow()
  90.     {
  91.         echo "Swallowing the banana.<br />";
  92.     }
  93.  
  94. }
  95. $fruit = 'apple';
  96.  
  97. $product = new $fruit();
  98.  
  99. $product->eat();
  100. ?>


4.1 Conclusion


In this tutorial we learned about class abstraction. We also learned the difference between an interface and an abstract class and when to use which.
  • Anonymous
  • Bot
  • No Avatar
  • Posts: ?
  • Loc: Ozzuland
  • Status: Online

Post 3+ Months Ago

Post Information

  • Total Posts in this topic: 1 post
  • Moderator: Tutorial Writers
  • Users browsing this forum: No registered users and 4 guests
  • You cannot post new topics in this forum
  • You cannot reply to topics in this forum
  • You cannot edit your posts in this forum
  • You cannot delete your posts in this forum
  • You cannot post attachments in this forum
 
 

© 1998-2014. Ozzu® is a registered trademark of Unmelted, LLC.