Flyweight Pattern

To minimise memory usage, a Flyweight shares as much as possible memory with similar objects. It is needed when a large amount of objects is used that don't differ much in state.


Purpose

To minimise memory usage, a Flyweight shares as much as possible memory with similar objects. It is needed when a large amount of objects is used that don't differ much in state. A common practice is to hold state in external data structures and pass them to the flyweight object when needed.

UML


Code

Text


namespace DesignPatterns\Structural\Flyweight;

/**
* This is the interface that all flyweights need to implement
*/ 
interface Text
{
  public function render(string $extrinsicState): string;
}

TextFactory


namespace DesignPatterns\Structural\Flyweight;

use Countable;

/**
* A factory manages shared flyweights. Clients should not instantiate them directly,
* but let the factory take care of returning existing objects or creating new ones.
*/
  class TextFactory implements Countable
  {
  /**
   * @var Text[]
   */
  private array $charPool = [];

  public function get(string $name): Text
  {
      if (!isset($this->charPool[$name])) {
          $this->charPool[$name] = $this->create($name);
      }

      return $this->charPool[$name];
  }

  private function create(string $name): Text
  {
      if (strlen($name) == 1) {
          return new Character($name);
      } else {
          return new Word($name);
      }
  }

  public function count(): int
  {
      return count($this->charPool);
  }
}

Word


namespace DesignPatterns\Structural\Flyweight;

class Word implements Text
{
    public function __construct(private string $name) { }

    public function render(string $extrinsicState): string
    {
        return sprintf('Word %s with font %s', $this->name, $extrinsicState);
    }
}

Character


namespace DesignPatterns\Structural\Flyweight;

/**
* Implements the flyweight interface and adds storage for intrinsic state, if any.
* Instances of concrete flyweights are shared by means of a factory.
*/
class Character implements Text
{
    /**
    * Any state stored by the concrete flyweight must be independent of its context.
    * For flyweights representing characters, this is usually the corresponding character code.
    */
    * 
    public function __construct(private string $name) { }

    public function render(string $extrinsicState): string
    {
         // Clients supply the context-dependent information that the flyweight needs to draw itself
         // For flyweights representing characters, extrinsic state usually contains e.g. the font
         return sprintf('Character %s with font %s', $this->name, $extrinsicState);
     }
}

In the code above, we create the flyweight classes via a Factory, then pass in the font at a later stage (when its needed). i.e


TextFactory::create('Stu')->render('TIMES_NEW_ROMAN');
// Outputs 'Word Stu with font TIMES_NEW_ROMAN'

TextFactory::create('S')->render('TIMES_NEW_ROMAN');
// Outputs 'Character S with font TIMES_NEW_ROMAN'