Design patterns in Action: Flyweight and Enumerated types

Flyweight pattern is not a pattern that you will come across very often in web applications. Most web applications are just serving incoming HTTP requests, a task that lasts just a few milliseconds. So, memory usage is not really a consideration for their developers. However, there is one area where you can see this pattern used: the implementation of enumerated types.

One of the most popular implementations of enumerated types in PHP is from MyCLabs: https://github.com/myclabs/php-enum and this is a stripped down version of the Enum class:

namespace MyCLabs\Enum;

abstract class Enum
{
    protected $value;

    protected static $cache = [];

    public function __construct($value)
    {
        if (!$this->isValid($value)) {
            throw new \UnexpectedValueException("Value '$value' is not part of the enum " . \get_called_class());
        }

        $this->value = $value;
    }

    public static function toArray()
    {
        $class = \get_called_class();
        if (!isset(static::$cache[$class])) {
            $reflection            = new \ReflectionClass($class);
            static::$cache[$class] = $reflection->getConstants();
        }

        return static::$cache[$class];
    }

    public static function isValid($value)
    {
        return \in_array($value, static::toArray(), true);
    }

    public static function __callStatic($name, $arguments)
    {
        $array = static::toArray();
        if (isset($array[$name]) || \array_key_exists($name, $array)) {
            return new static($array[$name]);
        }

        throw new \BadMethodCallException("No static method or enum constant '$name' in class " . \get_called_class());
    }
}

It will make it easier to you to understand the workings of this class by having in mind how this class is used. Let’s see an example:

namespace App\Enumerations;

use MyCLabs\Enum\Enum;

class ChannelType extends Enum {
    const PUBLIC = 'public';

    const PRIVATE = 'private';

    const PERSONAL = 'personal';
}

So, as you may have realised by now, Enum class is caching the list of constants that are defined for each enumerated type (each class that extends Enum).

The flyweight pattern can also by found in the “Type” class of Doctrine\DBAL (Doctrine\DBAL\Types\Type). The use of the flyweight pattern in this class is also mentioned in the methods comments, probably, to point those who are familiar with the pattern to the right direction:

/**
     * Factory method to create type instances.
     * Type instances are implemented as flyweights.
     *
     * @param string $name The name of the type (as returned by getName()).
     *
     * @return \Doctrine\DBAL\Types\Type
     *
     * @throws DBALException
     */
    public static function getType($name)
    {
        if (! isset(self::$typeRegistry[$name])) {
            throw DBALException::unknownColumnType($name);
        }

        return self::$typeRegistry[$name];
    }