Types, Interfaces and Abstracts

11th January, 2019

Types

Types are an important concept of OOP. A type refers to a behavioral characteristic of a class, object or interface. There are a few basic scalar types built into the language - boolean, integer, float, string, array, object, resource and null, and it is probable that you will create your own custom types. In fact, every object you create will have at least its own type - the objects class name. It may have more than one type if it implements one or more interface types. If it extends another class it will also inherit the type(s) from that parent class.

Some languages only support strict typing, like Java or Scala - where types have to be declared. Some languages don't have any type system, like Ruby or JavaScript. PHP sits in the middle: you can have some typing, or choose to use strict typing everywhere (drastically improved in PHP 7) or use very-little-to-no typing, it's up to the developer.

The purpose of a type is it allows you to write your code in an abstract, flexible manor (with some certainty that the code will at least be compatible). This is because instead of introducing dependencies in your code that depend on a specific, concrete class, you can be more flexible and just depend on the parts or types you need at that point in time. At runtime, your code doesn't care about what specific object it receives, just so long as it meets the type dependency.

I say the above with some certainty is because just because an object has a type, it doesn't guarantee it to be bug free or correct, it just means that the application won't even compile if the types don't match. If you don't use types then programmers can pass any object as any parameter, and if a method or property doesn't exist then an exception will probably be raised.

Interfaces

"Interface" has many occurrences in software, user interface (UI), application programming interface (api), a PHP interface. Essentially they all imply the same thing - a predefined, set of ways a user or client can manipulate or interrogate something.

Interfaces in PHP look a bit like classes, but with two major differences - they only have method names (they don't contain implementation details), and they cannot be instantiated and made an object.

There are two particularly useful uses for interfaces. One is for abstracting a type, or sharing behavior. The other is for separating code at boundaries.

As an example, a MediaPlayer might play multiple formats or types of Media, including MP3, WAV, AIFF. Each of these types will probably have different instructions and properties relating to decoding, sample rate, channel splitting and D/A conversion but we can leave all those nitty-gritty implementation details up to the individual type. All we care about from the MediaPlayers perspective is that we can play(Media).

class MediaPlayer
{
    public function play(Media $media)
    {
        $media->play();
    }
}
interface Media
{
    public function play();
}
class MP3 implements Media
{
    public function play()
    {
        // specific instructions to decode/play an mp3.
    }
}
class WAV implements Media
{
    public function play()
    {
        // specific instructions to decode/play an wav.
    }
}
class AIFF implements Media
{
    public function play()
    {
        // specific instructions to decode/play an aiff.
    }
}

To demonstrate an example of using an interface at a boundary think about a persistence layer. Lets say you have a RecordCollection that you want to store Records in. Lets make RecordCollection an interface, as we're not sure yet how we're going to implement it, but we know it should have at least a store(Record) method.

<?php

interface RecordCollection
{
    public function store(Record $record);
}

Lets also have a basic Record. For demo purposes the record won't be fully coded.

<?php

class Record {
    public $artist;
}

The first case we might need to create an implementation of the RecordCollection might be in our test suite (especially if we're doing TDD). Instead of using a database for persistence, for speed and performance we could simply use an in memory array. This will allow us to test against our RecordCollection, but after the request the array will be lost - it is not persistent.

<?php

class InMemoryRecordCollection implements RecordCollection
{
    private $records = [];

    public function store(Record $record)
    {
        $this->records[] = $record;
    }
}

Okay, so the above implementation is probably fine for tests or doing things on-the-fly, but what if we want long term persistence? Well, we could use a database, but that still seems a bit overkill. Lets use a regular flat file, loading and saving the records to disk on every request.

<?php

class FileSystemRecordCollection implements RecordCollection
{
    private $storageFile;
    private $records = [];

    public function __construct($filePath)
    {
        $this->storageFile = $filePath;
        $this->records = unserialize(file_get_contents($filePath)) ?: [];
    }

    public function __sleep()
    {
        file_put_contents($this->storageFile, serialize($this->records));
    }

    public function store(Record $record)
    {
        $this->records[] = $record;
    }
}

Now we have a working persistent solution. This is very similar to the InMemoryRecordCollection, but when it's loaded it reads all the Records that are saved in the file into an array, and when it is destroyed (when the PHP request is finished) the array is serialized back to the file. (In reality you may wish to add some error checking).

But what happens if your system needs to have a database version for some reason, maybe other applications use it, or the file system isn't powerful enough to support concurrent connections. Ok, then you may have a database implementation, something like so:

<?php

class DBRecordCollection implements RecordCollection
{
    private $db;

    public function __construct(PDO $db)
    {
        $this->db = $db;
    }

    public function store(Record $record)
    {
        $this->db
            ->prepare("INSERT INTO record_collection ...")
            ->bindParam('artist', $record->artist)
            ->execute();
    }
}

Obviously, the SQL above is purely for demonstration purposes only

And voilà, we have a database implementation. The point is that our interface never changed, and we can depend on that interface throughout our code base without worrying about what storage mechanism is used.

Abstracts

Abstracts are like an interface in that they can't be constructed, but are somewhat like a class in that the methods can contain implementation code. To instantiate an abstract you must create a concrete class that extends the abstract. This will have to implement all the abstract or undefined methods from the parent abstract method. Sounds complex, but maybe an illustration will help:

<?php

abstract class Quadrilateral
{
    protected $width;
    protected $height;

    public function __construct($width, $height)
    {
        $this->width = $width;
        $this->height = $height;
    }

    abstract public function area();

    public function perimeter()
    {
        return $this->width * 2 + $this->height * 2;
    }
}

Above we have our abstract class - note the abstract keyword before class. This means we cannot initialize it with $shape = new Quadrilateral();. Instead we will have to extend it. Also notice that there are some predefined methods, __construct, area and perimeter. Perimeter has a default implementation so doesn't need to be redefined in the child subclasses. Area has no implementation and is prefixed with abstract so this will have to be defined in any subclass.

Lets make three subclasses of Quadrilateral, namely Square, Rectangle and Rhombus (Diamond)

<?php

class Square extends Quadrilateral
{
    public function __construct($width)
    {
        parent::__construct($width, $width);
    }

    public function area()
    {
        return $this->width ** 2;
    }
}

$square = new Square(2);
echo $square->area(); // outputs 4
echo $square->perimeter(); // outputs 8

Notice that our Square extends our base abstract Quadrilateral. We have to define area (which is just the width squared) but the perimeter doesn't need to be re-implemented, as it is implemented, and correct, in our abstract class. Note we have overridden the constructor as well, as a Square must have equal height and width, we don't need to redeclare both parameters. To override a method, just redeclare it in the child concrete class. If you want to access the original or parent method, then access it through parent::methodName($parameters), as demonstrated in the Square constructor.

<?php

class Rectangle extends Quadrilateral
{
    public function area()
    {
        return $this->width * $this->height;
    }
}

$rectangle = new Rectangle(3, 6);
echo $rectangle->area(); // outputs 18
echo $rectangle->perimeter(); // outputs 18

Our rectangle above is very close to a quadrilateral, so we don't need to override much, but we do still need to define the area method, which is just the width * height.

<?php

class Rhombus extends Quadrilateral
{
    public function area()
    {
        return $this->width * $this->height / 2;
    }

    public function perimeter()
    {
        $side = sqrt($this->width ** 2 + $this->height ** 2);
        return $side * 4;
    }
}

$rhombus = new Rhombus(3, 4);
echo $rhombus->area(); // outputs 6
echo $rhombus->perimeter(); // outputs 20

Our final shape, the Rhombus, (or squashed square turned 45˚ on its side) doesn't need to override the constructor (here I'm referring to the diagonals between the corners as width and height, not the edges). It must implement area, which is the product of the diagonals divided by 2. It has overridden the perimeter because although it was defined in the abstract, we want to call different code, namely find the length of one side and multiply by 4.

Tagged: oop php design


Object Oriented Programming in PHP

11th January, 2019

I had a question from one of the people I'm mentoring on http://phpmentoring.org.

How do I decide what is an object and what should be a method?

This may seem a trivial question to someone who as been writing object orientated (OO) code for a while, but I can remember learning about objects and being perplexed by their properties, inheritance and much more when I started learning.

This is intended to be an introductory level article, some basic knowledge of PHP is assumed but not much OO experience.

Firstly, what is an object?

Put simply, it is something in your application that can have both state (properties) and also behavior (methods) that usually represents something in real life (we call this modeling our domain).

We can design objects wherever it makes sense to: for introducing a new piece of logic, or creating a feature or part of it. A well designed object should do (or represent) one thing - this is called SRP or the Single Responsibility Principle. There is no limit to the number of objects we can create or use in an applications, it is often better to have lots of small, simple objects that can be composed together to make up a larger application.

Smaller, simpler objects are:

  • easier to understand, read and reason about
  • easier to re-use (in this application and other applications)
  • easier to change or replace in the future
  • easier to unit test
  • easier to name if it has only one responsibility or capability
  • will probably have less dependencies on other code
  • easier to debug when things go wrong

Properties and Methods

Properties are like variables that belong to the object. They can be set to any primitive types (strings, integers, floats, arrays) or even other objects, and they can change or mutate over time.

Methods are similar to functions except they belong, or relate, to the object. They are normally split into imperative (commanding) or interrogative (questioning) categories, and ideally each method will do one thing only.

Properties and methods can have different visibilities (public, private or protected) which alter the scope of the property or method, and affect how other objects can interact with it.

  • public means fully accessible to any other objects or calls outside of the object itself.
  • private means only accessible within the object (i.e. from internal methods). No outsider can access the private properties or methods.
  • protected is similar to private, except if another class extends an object with protected properties/methods then these can be accessed from the child as well as the parent.

What is a class?

A class is often called the blueprint for an object. An object is an instance of a class. To create an instance of a class (object) you typically use the new keyword, such as new MyClass();. Using the new keyword calls a special reserved method on the class called __construct(). The constructor may take parameters or set up internal properties of the object if needs be. Any parameters or dependencies the object needs to be considered valid should be passed at construction time, otherwise you may end up with invalid or incomplete objects.

Defining a class

<?php

class Student
{
    private $name;
    private $dob;

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

    public function age(): Int
    {
        return $this->dob->diff(new DateTime())->y;
    }
}

Using the above class

<?php

$pete = new Student('Pete', new DateTime('1 June 1988'));

echo $pete->age();

Tagged: oop php design mentoring


Naming things

11th January, 2019

Without sounding too pretentious, I think naming things well is a skill that comes with experience. As a software designer you are given the complete freedom to name your variables, functions and classes however you choose, within the constraints of the language.

Here are some tips from lessons I've learned:

  • be clear - give things their proper name. Name things how you would talk about them with the domain experts and users. Don't give variables or methods one letter names. The only exceptions may be when in a very arithmetic heavy calculations, for example, substituting $w and $h for $width and $height may be okay.
  • be specific where necessary - I used to work on a system where every collection or array was called $data. If you have a collection of Person items, please don't name it $array or $data, what's wrong with $people?!
  • be abstract where necessary - if you're naming an abstract class or interface, try to be generic, give it the abstract name you might use when talking about it in conversation.
  • question existing names and conventions - how does it sound when you read the method? Does it flow? Could you speak it in conversation?
  • use conventions - in PHP methods and properties are written in camelCase(). Classes and Namespaces should use StudlyCase. If in doubt, the PSR2 standards is a good starting place.
  • be consistent - if a method is command, it should sound like a command, e.g. sendEmail(). If a method is interrogative, it will probably be prefixed with get*, find*, is*, or has*. get* implies a retrieval of something normally instantiated on an object, like a property. find* implies that some computation or searching may be necessary in order to retrieve something. is* and has* both imply a boolean true/false return type so they may be used in conditionals, for example if ($object->isValid()) {...} and if ($post->hasComments()) {...}.
  • be unambiguous - $list->empty() - does this imply a question to see if a list is empty, or a command to empty the list?
  • don't needlessly repeat types - for example $basket->addToBasket($product) would be better as $basket->add($product).
  • comments are code smells I find if I need to comment something it's because my code is so complicated I can't follow it or remember how it's working. This is a definite code smell, but one that is easy to fix by either extracting a complicated logic into a method or variable and using the old comment as a method or variable name.
  • Don't be afraid to rename things - it is super easy to rename things especially with an IDE like phpStorm. If something doesn't sound quite right or if something changes you are free to change method/variable/class names to reflect that change.

Do you have any other naming tips or things that you hate seeing in other code bases? Please share in the comments below and I'll update my list!

Tagged: oop php design