PHP OOP - Polymorphism - Abstract classes

Last revision:

Abstract classes

By now you understand that you use classes to create objects that have specific properties and methods.

Creating an object of a certain class is called instantiating that class, or instantiating an object of that class.

Abstract classes are a special kind of class, used to promote polymorphism code reuse.

Abstract classes are classes that can not be instantiated directly: you can't create objects of an abstract class. Abstract classes must be subclassed, another word for extended. 

Put differently: abstract classes can only serve as parent classes.

To make a class abstract, use the abstract keyword:

abstract class Connection {

  // Implementation goes here.

}

Let's say you have an abstract class Connection, and a normal class DatabaseConnection that extends Connection.

In that scenarion:

  • Allowed: $db_connection = new DatabaseConnection();
     
  • ERROR: $my_connection  = new Connection();

From concrete to abstract

Assume you have a normal Animal class with a speak() method, and a normal Dog class that extends Animal:

// Concrete classes.

class Animal {

  public function speak() {
    print("Hello.");
  }

}

class Dog extends Animal {

  // ...

}

Let's also say you have no intention of ever instantiating Animal objects, only objects like Dog and Cat that extend Animal.

You could implement the speak() method on Animal to print "hello", and assume that the subclasses Dog and Cat will each override speak() to print "Woof" and "Miau" respectively:

Code
<?php

// Concrete classes.

class Animal {

  public function speak() {
    print("Hello.");
  }

}

class Dog extends Animal {

  public function speak() {
    print("Woof.");
  }
  
}

$my_dog = new Dog();
$my_dog->speak();

That would work, but if the subclasses don't override speak(), they will print "hello", which does not make sense for Dogs or Cats.

Also, having a fully implemented speak() method on Animal, a class that you never intend to instantiate, feels incorrect and wasteful: why provide an initial implementation that will be overridden by every child class?

In other words, the default implementation of speak() on the parent class is not useful or appropriate to be used by the child classes.

Ideally you want to force the child classes to declare and implement speak() themselves, without having the parent class implement it.

This is where the abstract keyword comes in. You can apply it to classes and methods:

  • Abstract classes are classes that use the abstract keyword and define at least one abstract method.
  • Abstract methods are methods that use the abstract keyword and have a name but no implementation. They define a method signature (name and optional list of parameters) but don't define a body (no actual implementation).

Example

The following example shows an abstract class Animal with an abstract method speak() and a concrete (normal) method sleep().
 

Code
<?php
// Abstract class.
abstract class Animal {

  public abstract function speak();

  public function sleep() {
    print("Sleeping." . PHP_EOL);
  }

}


// Concrete class.
class Dog extends Animal {

  public function speak() {
    print("Woof." . PHP_EOL);
  }

}

$my_dog = new Dog();
$my_dog->speak();
$my_dog->sleep();

The Animal class is defined as abstract. 

It defines one abstract method named speak(). Because that method is defined as abstract, the method is not allowed to provide a body or implementation.

Because Animal is an abstract class, it cannot be instantiated itself; it can only be subclassed. 
Dog does that: Dog extends Animal.

Normally Dog would inherit the speak() method from Animal, but that's an abstract method, so there is nothing to inherit. Instead, Dog MUST implement speak() itself, or an error will be thrown.

Animal also provides the concrete method sleep(), which is inherited by Dog, and can be overridden.

Rules

  • Abstract classes and functions use the abstract keyword.
  • Abstract classes must provide at least one abstract method.
  • A child class that inherits from an abstract class MUST implement all of the parent's abstract methods.
     

Concrete versus abstract

Code
<?php
class Animal {

  public function speak() {
    print("Hello" . PHP_EOL);
  }

  public function sleep() {
    print("Sleeping." . PHP_EOL);
  }

}


class Dog extends Animal {

  public function speak() {
    print("Woof." . PHP_EOL);
  }

}

$myanimal = new Animal();
$myanimal->speak();
$myanimal->sleep();

$mydog = new Dog();
$mydog->speak();
$mydog->sleep();
Code
<?php
abstract class Animal {

  public abstract function speak();

  public function sleep() {
    print("Sleeping." . PHP_EOL);
  }

}

class Dog extends Animal {

  public function speak() {
    print("Woof." . PHP_EOL);
  }

}

// This will throw an ERROR:
$myanimal = new Animal();

// This will work correctly:
$mydog = new Dog();
$mydog->speak();
$mydog->sleep();

Usage

You can see abstract classes as a sort of  blueprint for other classes, with the difference that you can only instantiate child classes, not the abstract class itself.

Abstract classes specify abstract methods that MUST be implemented by child classes.

Abstract classes can specify concrete methods that can serve as useful default implementations.

Abstract classes cannot be instantiated; they can only be subclassed / inherited from.

So abstract classes can be useful when you want to represent different variations of the same thing (Dogs and Cats are all "variations" of Animal), and need to be able to rely on certain methods and properties being present, but don't want to create objects of the "main" class, only of the child classes.
 

Disadvantages

You cannot instantiate abstract classes. In our Animal example, if you do have a need to represent a generic animal, you would have to create something like a GenericAnimal child class, which is more work and may feel incorrect.

You must implement all of the abstract methods from the abstract class you're extending, even if you don't need those in your class definition. This has the potential to waste a lot of time and effort.

If you need to make changes to your abstract class, you'll need to make the same changes to all the subclasses as well, which can make everything less flexible and harder to manage.
 

Inheritance or composition?

Lastly, sometimes you do want to reuse code, but don't want to use the concept of parent/child classes because it doesn't fit with your use case.

For example: you have classes that represent humans, animals, physical objects, and conceptual things like love, patience, and strength. You want all of your classes to be drawable (have a draw() method) and describable (have a describe() method).

You could use a parent class - abstract or concrete - and create child classes such as Human, Animal, Book, Vehicle, and Concept.

Would it make sense that classes like Animal, Human, and Vehicle all have a common parent class? And what would you name that common parent class? Thing?

You can see that the idea of using parent/child classes in this case feels wrong:  there is no logical hierarchy between Things, Humans, Animals, Vehicles, or Concepts like love and happiness.

There's a different approach to ensure tat multiple classes share functionality and be predictable without using inheritance: interfaces. 

See the section on interfaces in PHP to learn more.

Summary

  • Abstract classes and methods use the abstract keyword.
  • Abstract classes must have at least one abstract method.
  • Abstract methods don't have a body / implementation.
  • Each class that extends an abstract class must implement all of the abstract methods declared by the abstract class.
  • Abstract classes cannot be instantiated; they can only be subclassed.
  • Abstract classes are used when you want to create a parent class that you will never instantiate, but will use as starting point for the creation of subclasses.
  • Abstract classes usually contain a combination of abstract methods and concrete methods.
  • "Standard", non-abstract classes / methods are called concrete classes / methods.