Inheritance Chain

Tracing the Lineage of Functionality in Inheritance Chains

In the last article I mentioned briefly that you can’t extend multiple classes at once. But the parent class can itself be a child class and extend a class of its own. In this article, we’ll be cleaning up our Dog class. We’ve pretty much moved everything from the GermanShepherd class into the Dog class, but there are a few properties and methods that can venture even further up to Mammal and Animal classes.

Before we get into that, let’s review Inheritance with the Car class that we didn’t cover in the previous article.

https://blog.devgenius.io/php-p53-class-inheritance-e743de094a06

The Car class contains constants like HAS_HEADLIGHTS, HAS_TAIL_LIGHTS, and HAS_TURN_SIGNALS. If you think about cars in general, each car will have headlights, tail lights, and turn signals, but not every vehicle will have those. So, we’ll leave those inside of the Car class.

<?php

class Vehicle {
}

class Car extends Vehicle {
    const HAS_HEADLIGHTS = true;
    const HAS_TAIL_LIGHTS = true;
    const HAS_TURN_SIGNALS = true;
  
    //...
}

Now, let’s take a look at our Car properties. Each vehicle will contain a year, make, and model, but not every vehicle needs to run on fuel: think horse-carriages. We’ll leave the fuel_type property inside of the Car object. Similarly, horsepower, torque, transmission, vehicle_type (whether it’s a coupe or sedan: not a great property name I know), and car_on can stay inside of the Car class. The dimensions can move up to the Vehicle class because each vehicle will have dimensions. Since the private properties are moving their way up to Vehicle, we’ll need to change their visibility modifier to protected if we want to access them from the Car class, which we do.

<?php

class Vehicle {
    protected string $color;
    protected string $make;
    protected string $model;
    protected int $year;
    protected float $exterior_height;
    protected float $exterior_width;
    protected float $exterior_length;
    protected string $exterior_um;
    protected float $weight;
    protected string $weight_um;
}

class Car extends Vehicle {
    const HAS_HEADLIGHTS = true;
    const HAS_TAIL_LIGHTS = true;
    const HAS_TURN_SIGNALS = true;
  
    //...
}

The accessors and mutators for the properties that we moved up to the Vehicle class can also be moved to the Vehicle class.That completes the movement of Car class elements to the Vehicle class.

<?php
class Vehicle {
    protected string $color;
    protected string $make;
    protected string $model;
    protected int $year;
    protected float $exterior_height;
    protected float $exterior_width;
    protected float $exterior_length;
    protected string $exterior_um;
    protected float $weight;
    protected string $weight_um;

    /**
     * @return string
     */
    public function getColor(): string
    {
        return $this->color;
    }

    /**
     * @param string $color
     */
    public function setColor(string $color): void
    {
        $this->color = $color;
    }

    /**
     * @return string
     */
    public function getMake(): string
    {
        return $this->make;
    }

    /**
     * @param string $make
     */
    public function setMake(string $make): void
    {
        $this->make = $make;
    }

    /**
     * @return string
     */
    public function getModel(): string
    {
        return $this->model;
    }

    /**
     * @param string $model
     */
    public function setModel(string $model): void
    {
        $this->model = $model;
    }

    /**
     * @return int
     */
    public function getYear(): int
    {
        return $this->year;
    }

    /**
     * @param int $year
     */
    public function setYear(int $year): void
    {
        if ( $year < 0 ) {
            echo "Not a valid year";
            return;
        }

        $this->year = $year;
    }

    /**
     * @return float
     */
    public function getExteriorHeight(): float
    {
        return $this->exterior_height;
    }

    /**
     * @param float $exterior_height
     */
    public function setExteriorHeight(float $exterior_height): void
    {
        $this->exterior_height = $exterior_height;
    }

    /**
     * @return float
     */
    public function getExteriorWidth(): float
    {
        return $this->exterior_width;
    }

    /**
     * @param float $exterior_width
     */
    public function setExteriorWidth(float $exterior_width): void
    {
        $this->exterior_width = $exterior_width;
    }

    /**
     * @return float
     */
    public function getExteriorLength(): float
    {
        return $this->exterior_length;
    }

    /**
     * @param float $exterior_length
     */
    public function setExteriorLength(float $exterior_length): void
    {
        $this->exterior_length = $exterior_length;
    }

    /**
     * @return string
     */
    public function getExteriorUm(): string
    {
        return $this->exterior_um;
    }

    /**
     * @param string $exterior_um
     */
    public function setExteriorUm(string $exterior_um): void
    {
        $this->exterior_um = $exterior_um;
    }

    /**
     * @return float
     */
    public function getWeight(): float
    {
        return $this->weight;
    }

    /**
     * @param float $weight
     */
    public function setWeight(float $weight): void
    {
        $this->weight = $weight;
    }

    /**
     * @return string
     */
    public function getWeightUm(): string
    {
        return $this->weight_um;
    }

    /**
     * @param string $weight_um
     */
    public function setWeightUm(string $weight_um): void
    {
        $this->weight_um = $weight_um;
    }

    protected function get_make_and_model(): string
    {
        return $this->make . " " . $this->model;
    }
}

// Car Class
class Car extends Vehicle
{

    const HAS_HEADLIGHTS = true;
    const HAS_TAIL_LIGHTS = true;
    const HAS_TURN_SIGNALS = true;

    private int $fuel_type;
    private int $hp;
    private int $tq;
    private string $transmission;
    private string $vehicle_type;
    private bool $car_on;

    public function __construct(int $year,
                                string $make,
                                string $model,
                                string $color = "",
                                int $fuel_type = 93,
                                int $hp = -1,
                                int $tq = -1,
                                string $transmission = "5 Speed Manual",
                                string $vehicle_type = "",
                                float $exterior_height = -1,
                                float $exterior_width = -1,
                                float $exterior_length = -1,
                                string $exterior_um = "in",
                                float $weight = -1,
                                string $weight_um = "lbs",
                                bool $car_on = false
    )
    {
        $this->year = $year;
        $this->make = $make;
        $this->model = $model;
        $this->color = $color;
        $this->fuel_type = $fuel_type;
        $this->hp = $hp;
        $this->tq = $tq;
        $this->transmission = $transmission;
        $this->vehicle_type = $vehicle_type;
        $this->exterior_height = $exterior_height;
        $this->exterior_width = $exterior_width;
        $this->exterior_length = $exterior_length;
        $this->exterior_um = $exterior_um;
        $this->weight = $weight;
        $this->weight_um = $weight_um;
        $this->car_on = $car_on;

        echo "Connection Successfully Established";
    }

    /**
     * @return int
     */
    public function getFuelType(): int
    {
        return $this->fuel_type;
    }

    /**
     * @param int $fuel_type
     */
    public function setFuelType(int $fuel_type): void
    {
        $this->fuel_type = $fuel_type;
    }

    /**
     * @return int
     */
    public function getHp(): int
    {
        return $this->hp;
    }

    /**
     * @param int $hp
     */
    public function setHp(int $hp): void
    {
        $this->hp = $hp;
    }

    /**
     * @return int
     */
    public function getTq(): int
    {
        return $this->tq;
    }

    /**
     * @param int $tq
     */
    public function setTq(int $tq): void
    {
        $this->tq = $tq;
    }

    /**
     * @return string
     */
    public function getTransmission(): string
    {
        return $this->transmission;
    }

    /**
     * @param string $transmission
     */
    public function setTransmission(string $transmission): void
    {
        $this->transmission = $transmission;
    }

    /**
     * @return string
     */
    public function getVehicleType(): string
    {
        return $this->vehicle_type;
    }

    /**
     * @param string $vehicle_type
     */
    public function setVehicleType(string $vehicle_type): void
    {
        $this->vehicle_type = $vehicle_type;
    }

    /**
     * @return bool
     */
    public function isCarOn(): bool
    {
        return $this->car_on;
    }

    /**
     * @param bool $car_on
     */
    public function setCarOn(bool $car_on): void
    {
        $this->car_on = $car_on;
    }

    public function turnOn(): string
    {
        if ($this->car_on) {
            return "The " . $this->get_make_and_model() . " is already on.";
        }

        $this->car_on = true;
        return $this->get_make_and_model() . " has been turned on.";
    }

    public function turnOff(): string
    {
        if ($this->car_on) {
            $this->car_on = false;
            return "The " . $this->get_make_and_model() . " has been turned off.";
        }

        return "The " . $this->get_make_and_model() . " is already off.";
    }

    public function drive(): string
    {
        if ($this->car_on) {
            return "I'm driving";
        } else {
            return "You gotta turn me on";
        }
    }

    public function get_car_details(): array
    {
        return [
            "year" => $this->year,
            "make" => $this->make,
            "model" => $this->model,
            "color" => $this->color,
            "fuel_type" => $this->fuel_type,
            "hp" => $this->hp,
            "torque" => $this->tq,
            "transmission" => $this->transmission
        ];
    }

    public function __destruct()
    {
        echo "Connection Successfully Closed";
    }
}

$chevy = new Car(1997, "Chevy", "Camaro");
$chevy->setHp(900);
$chevy->setTq(1000);

echo $chevy->getHp();
echo $chevy->getTq();

$chevy->setYear(1999);
echo $chevy->turnOn();
echo $chevy->drive();
echo $chevy->turnOff();

Next up is the Dog class. We created the Dog class by moving some of the elements up from the GermanShepherd class. If we wanted to, and we do, we can keep extending classes from child to parent. For this example, we’ll create two more classes: Mammal and Animal. Dog will inherit from Mammal and Mammal will inherit from Animal. As you can clearly see, classes can be children and parents at the same time. The Dog class is GermanShepherd’s parent, but it’s the child of Mammal.

<?php

class Animal {
}

class Mammal extends Animal {
}

class Dog extends Mammal
{
    const HAS_TAIL = true;
    //...

    protected $does_shed = true;
    protected $breed;
    protected $fur_color;

    //...
}

class GermanShepherd extends Dog
{

    public function __construct(string $eye_color,
                                string $dob,
                                string $fur_color)
    {
        $this->eye_color = $eye_color;
        $this->dob = $dob;
        $this->fur_color = $fur_color;
    }

    public function bark()
    {
        echo "Loud Barking";
    }
}

We don’t have to look at our GermanShepherd class anymore since we’ve already moved everything that can be moved up to the Dog class. Looking at our Dog class, however, we can see that there are a few elements that can be moved up, such as the HAS_HEART constant. It doesn’t make sense to move the HAS_TAIL up since not all Mammals nor Animals have tails. The HAS_HEART constant can move its way up to the Animal class.

<?php

class Animal {
    const HAS_HEART = true;
}

class Mammal extends Animal {
}

class Dog extends Mammal {
  
    const HAS_TAIL = true;

    protected $does_shed = true;
    protected $breed;
    protected $fur_color;

    //...
}

class GermanShepherd extends Dog {
  
    public function __construct(string $eye_color,
                                string $dob,
                                string $fur_color)
    {
        $this->eye_color = $eye_color;
        $this->dob = $dob;
        $this->fur_color = $fur_color;
    }

    public function bark()
    {
        echo "Loud Barking";
    }
}

Eye Color is something that all mammals would have since an animal does not need to have eyes (I think). The date of birth is an animal characteristic, so we can move the DOB all the way up to the Animal class.

The phylum, class, order, etc, can be moved up to the Animal class, but they will need to be initialized later since not all Animals fall under the same classification. Kingdom should be kept as Animalia.

<?php

class Animal {
    const HAS_HEART = true;

    protected $dob;
    protected $kingdom = "Animalia";
    protected $phylum;
    protected $class;
    protected $order;
    protected $family;
    protected $genus;
    protected $species;
    protected $subspecies;
}

class Mammal extends Animal {

    protected $eye_color;
}

class Dog extends Mammal
{
    const HAS_TAIL = true;

    protected $does_shed = true;
    protected $breed;
    protected $fur_color;

    //...
}

class GermanShepherd extends Dog
{

    public function __construct(string $eye_color,
                                string $dob,
                                string $fur_color)
    {
        $this->eye_color = $eye_color;
        $this->dob = $dob;
        $this->fur_color = $fur_color;
    }

    public function bark()
    {
        echo "Loud Barking";
    }
}

For each of the properties that we moved up, we can also move up their getter and setter methods. Sleeping seems like an animal thing, so we’ll move the sleep() method into the Animal class. Finally, since we haven’t generated the getters and setters for the classification characteristics, we’ll create those now (i.e. getKingdom()). The only method that we won’t add is the setKingdom() inside of our Animal class, since we don’t want anyone to modify our Animal kingdom from Animalia.

And that’s it. Our GermanShepherd now has access to properties and methods from Dog, Mammal, and Animal classes. We also set properties inside of the parent classes directly from our GermanShepherd class. For example, the date of birth is passed when the GermanShepherd class is instantiated and is set inside of the Animal class. We can run the same tests that we ran before and we should get the exact same results. Not bad, inheritance.

Continue your learning with these articles

Class Inheritance

PASSING DOWN CODE WISDOM THROUGH GENERATIONS

PHP – P53: CLASS INHERITANCE

Who would have guessed that PHP supports inheritance? Everyone using PHP since Object Oriented Programming became a thing, that’s who.

Inheritance Chain

Tracing the Lineage of Functionality in Inheritance Chains

PHP – P54:INHERITANCE CHAIN

In the last article I mentioned briefly that you can’t extend multiple classes at once. But the parent class can itself be a child class and extend a class of its own.

File Separation

PIECING TOGETHER MASTERPIECES WITH INCLUDED FILES

PHP – P55: FILE SEPARATION

Include, Require, Require Once, and Include Once. You might have seen these at the top of a file in someone else’s code, but what does each one of them mean?

Leave a Reply