<?php
class CharacterFactory
{
private static $instance;
private $Characters = [];
public static function getInstance()
{
if (empty(self::$instance)) {
self::$instance = new CharacterFactory();
}
return self::$instance;
}
public function addCharacter($char)
{
if (!array_key_exists($char, $this->Characters)) {
$this->Characters[$char] = new Character($char);
}
}
public function getCharacters()
{
return $this->Characters;
}
public final function __clone()
{
throw new \Exception('This Instance is Not Clone');
}
public final function __wakeup()
{
throw new \Exception('This Instance is Not unserialize');
}
}
class Character
{
private $id;
private $character;
public function __construct($character)
{
$this->id = hash('sha256', time());
$this->character = $character;
}
public function getId()
{
return $this->id;
}
public function getCharacter()
{
return $this->character;
}
}
$chars = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z'
];
$factory = CharacterFactory::getInstance();
for ($i=0; $i<20; $i++)
{
$select_char = $chars[rand(0, 25)];
echo sprintf('%s / ', $select_char);
$factory->addCharacter($select_char);
}
echo '<hr>';
foreach ($factory->getCharacters() as $char_data)
{
echo sprintf('%s<br>', $char_data->getCharacter());
}
<?php
// // https://www.sitepoint.com/flyweight-design-pattern-immutability-perfect-match/
class File
{
private $data;
public function __construct($filePath)
{
// Check to make sure the file exists
if (!file_exists($filePath)) {
throw new InvalidArgumentException('File does not exist: '.$filePath);
}
$this->data = file_get_contents($filePath);
}
public function getData()
{
return $this->data;
}
}
class FileFactory
{
private $files = array();
public function getFile($filePath)
{
// If the file path isn't in our array, we need to create it
if (!isset($this->files[$filePath])) {
$this->files[$filePath] = new File($filePath);
}
return $this->files[$filePath];
}
}
// USAGE
$factory = new FileFactory;
$myLargeImageA = $factory->getFile('/path/to/my/large/image.png');
$myLargeImageB = $factory->getFile('/path/to/my/large/image.png');
if ($myLargeImageA === $myLargeImageB) {
echo 'Yay, these are the same object!'.PHP_EOL;
} else {
echo 'Something went wrong :('.PHP_EOL;
}
// another example
abstract class Type
{
const INTEGER = 'integer';
const STRING = 'string';
const DATETIME = 'datetime';
private static $_typeObjects = array();
private static $_typesMap = array(
self::INTEGER => 'Doctrine\DBAL\Types\IntegerType',
self::STRING => 'Doctrine\DBAL\Types\StringType',
self::DATETIME => 'Doctrine\DBAL\Types\DateTimeType',
);
public static function getType($name)
{
if (!isset(self::$_typeObjects[$name])) {
if (!isset(self::$_typesMap[$name])) {
throw DBALException::unknownColumnType($name);
}
self::$_typeObjects[$name] = new self::$_typesMap[$name]();
}
return self::$_typeObjects[$name];
}
// ...
}
// USAGE
$type1 = Type::getType(Type::INTEGER);
$type2 = Type::getType(Type::INTEGER);
if ($type1 === $type2) {
echo 'Yay, you used the flyweight pattern!'.PHP_EOL;
} else {
echo 'Well this is confusing :('.PHP_EOL;
}
<?php
interface Text
{
public function render(string $extrinsicState);
}
class Word implements Text
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function render(string $font)
{
return sprintf('Word %s with font %s', $this->name, $font);
}
}
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.
*/
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function render(string $font)
{
// 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, $font);
}
}
/**
* 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 $charPool = [];
public function get(string $name)
{
if (!isset($this->charPool[$name])) {
$this->charPool[$name] = $this->create($name);
}
return $this->charPool[$name];
}
private function create(string $name)
{
if (strlen($name) == 1) {
return new Character($name);
} else {
return new Word($name);
}
}
public function count()
{
return count($this->charPool);
}
}
// USAGE
$characters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
$fonts = ['Arial', 'Times New Roman', 'Verdana', 'Helvetica'];
$factory = new TextFactory();
for ($i = 0; $i <= 10; $i++)
{
foreach ($characters as $char)
{
foreach ($fonts as $font)
{
$flyweight = $factory->get($char);
$rendered = $flyweight->render($font);
}
}
}
foreach ($fonts as $word)
{
$flyweight = $factory->get($word);
$rendered = $flyweight->render('foobar');
}
<?php
//include "1.php";
/**
* The Flyweight stores a common portion of the state (also called intrinsic
* state) that belongs to multiple real business entities. The Flyweight accepts
* the rest of the state (extrinsic state, unique for each entity) via its
* method parameters.
*/
class Flyweight
{
private $sharedState;
public function __construct($sharedState)
{
$this->sharedState = $sharedState;
}
public function operation($uniqueState): void
{
$s = json_encode($this->sharedState);
$u = json_encode($uniqueState);
echo "Flyweight: Displaying shared ($s) and unique ($u) state.\n";
}
}
/**
* The Flyweight Factory creates and manages the Flyweight objects. It ensures
* that flyweights are shared correctly. When the client requests a flyweight,
* the factory either returns an existing instance or creates a new one, if it
* doesn't exist yet.
*/
class FlyweightFactory
{
/**
* @var Flyweight[]
*/
private $flyweights = [];
public function __construct(array $initialFlyweights)
{
foreach ($initialFlyweights as $state) {
$this->flyweights[$this->getKey($state)] = new Flyweight($state);
}
}
/**
* Returns a Flyweight's string hash for a given state.
*/
private function getKey(array $state): string
{
ksort($state);
return implode("_", $state);
}
/**
* Returns an existing Flyweight with a given state or creates a new one.
*/
public function getFlyweight(array $sharedState): Flyweight
{
$key = $this->getKey($sharedState);
if (!isset($this->flyweights[$key])) {
echo "FlyweightFactory: Can't find a flyweight, creating new one.\n";
$this->flyweights[$key] = new Flyweight($sharedState);
} else {
echo "FlyweightFactory: Reusing existing flyweight.\n";
}
return $this->flyweights[$key];
}
public function listFlyweights(): void
{
$count = count($this->flyweights);
echo "\nFlyweightFactory: I have $count flyweights:\n";
foreach ($this->flyweights as $key => $flyweight) {
echo $key . "\n";
}
}
}
/**
* The client code usually creates a bunch of pre-populated flyweights in the
* initialization stage of the application.
*/
$factory = new FlyweightFactory([
["Chevrolet", "Camaro2018", "pink"],
["Mercedes Benz", "C300", "black"],
["Mercedes Benz", "C500", "red"],
["BMW", "M5", "red"],
["BMW", "X6", "white"],
// ...
]);
$factory->listFlyweights();
// ...
function addCarToPoliceDatabase(
FlyweightFactory $ff, $plates, $owner,
$brand, $model, $color
) {
echo "\nClient: Adding a car to database.\n";
$flyweight = $ff->getFlyweight([$brand, $model, $color]);
// The client code either stores or calculates extrinsic state and passes it
// to the flyweight's methods.
$flyweight->operation([$plates, $owner]);
}
addCarToPoliceDatabase($factory,
"CL234IR",
"James Doe",
"BMW",
"M5",
"red"
);
addCarToPoliceDatabase($factory,
"CL234IR",
"James Doe",
"BMW",
"X1",
"red"
);
$factory->listFlyweights();
/*
Output.txt: Execution result
FlyweightFactory: I have 5 flyweights:
Chevrolet_Camaro2018_pink
Mercedes Benz_C300_black
Mercedes Benz_C500_red
BMW_M5_red
BMW_X6_white
Client: Adding a car to database.
FlyweightFactory: Reusing existing flyweight.
Flyweight: Displaying shared (["BMW","M5","red"]) and unique (["CL234IR","James Doe"]) state.
Client: Adding a car to database.
FlyweightFactory: Can't find a flyweight, creating new one.
Flyweight: Displaying shared (["BMW","X1","red"]) and unique (["CL234IR","James Doe"]) state.
FlyweightFactory: I have 6 flyweights:
Chevrolet_Camaro2018_pink
Mercedes Benz_C300_black
Mercedes Benz_C500_red
BMW_M5_red
BMW_X6_white
BMW_X1_red
*/
<?php
/*
Rules
------
Flyweight objects should never be created by the client, using the new keyword. Instances should always be created by using a factory class.
The factory class should store instances of each flyweight, similar to how a multiton stores references to objects in a static attribute.
Flyweight attributes which are not sharable (extrinsic) between all instances of the flyweight, should be supplied by the client and stored inside the flyweight object.
Flyweight attributes which are sharable (intrinsic), should be immutable and set into the flyweight by default.
The client/controller is responsible for creating the flyweights using the factory class and providing them to the domains models.
Advantages
----------
Effective use of the flyweight pattern can provide memory consumption benefits.
*/
interface WeaponInterface
{
public function __construct();
public function getBaseDamage();
public function getEnhancementsDamage();
public function getDamage();
public function reload();
public function addEnhancement(WeaponFlyweightInterface $enhancement);
}
class PlasmaRifle implements WeaponInterface
{
const TOTAL_AMMO_IN_PLAMA_SHELL = 10;
const MIN_DAMAGE = 50;
const MAX_DAMAGE = 75;
private $ammoRemaining = null;
private $enhancements = [];
public function __construct()
{
$this->reload();
}
public function getBaseDamage()
{
return rand(self::MIN_DAMAGE, self::MAX_DAMAGE);
}
public function getEnhancementsDamage()
{
$damage = 0;
foreach($this->enhancements as $enhancement) {
$damage += $enhancement->getDamage();
}
return $damage;
}
public function getDamage()
{
return $this->getBaseDamage() + $this->getEnhancementsDamage();
}
public function reload()
{
$this->ammoRemaining = self::TOTAL_AMMO_IN_PLAMA_SHELL;
}
public function addEnhancement(WeaponFlyweightInterface $enhancement)
{
$this->enhancements[] = $enhancement;
}
}
interface WeaponFlyweightInterface
{
public function getDamage();
}
class PlasmaRifleGrenadeLauncherFlyweight implements WeaponFlyweightInterface
{
const MIN_DAMAGE = 100;
const MAX_DAMAGE = 120;
public function getDamage()
{
return rand(self::MIN_DAMAGE, self::MAX_DAMAGE);
}
}
class PlasmaRifleExplosionFlyweight implements WeaponFlyweightInterface
{
const MIN_DAMAGE = 500;
const MAX_DAMAGE = 1000;
public function getDamage()
{
return rand(self::MIN_DAMAGE, self::MAX_DAMAGE);
}
}
class PlasmaRifleFlyweightFactory
{
private static $instances = [];
public static function factory($flyweight)
{
$className = "PlasmaRifle" . $flyweight . "Flyweight";
if(empty(self::$instances[$className])) {
self::$instances[$className] = new $className();
}
return self::$instances[$className];
}
}
$plasmaRifle = new PlasmaRifle();
$plasmaRifle->addEnhancement(PlasmaRifleFlyweightFactory::factory("GrenadeLauncher"));
$plasmaRifle->addEnhancement(PlasmaRifleFlyweightFactory::factory("GrenadeLauncher"));
$plasmaRifle->addEnhancement(PlasmaRifleFlyweightFactory::factory("GrenadeLauncher"));
$plasmaRifle->addEnhancement(PlasmaRifleFlyweightFactory::factory("GrenadeLauncher"));
$plasmaRifle->addEnhancement(PlasmaRifleFlyweightFactory::factory("GrenadeLauncher"));
$plasmaRifle->addEnhancement(PlasmaRifleFlyweightFactory::factory("Explosion"));
$plasmaRifle->addEnhancement(PlasmaRifleFlyweightFactory::factory("Explosion"));
$plasmaRifle->addEnhancement(PlasmaRifleFlyweightFactory::factory("Explosion"));
$plasmaRifle->addEnhancement(PlasmaRifleFlyweightFactory::factory("Explosion"));
$plasmaRifle->addEnhancement(PlasmaRifleFlyweightFactory::factory("Explosion"));
$plasmaRifle->addEnhancement(PlasmaRifleFlyweightFactory::factory("Explosion"));
// Shot damage can vary wildly due to the random factor in base damage and flyweight enhancements
echo "Shot 1 Damage: " . $plasmaRifle->getDamage() . PHP_EOL; # Shot 1 Damage: 3944
echo "Shot 2 Damage: " . $plasmaRifle->getDamage() . PHP_EOL; # Shot 2 Damage: 4637
echo "Shot 3 Damage: " . $plasmaRifle->getDamage() . PHP_EOL; # Shot 3 Damage: 4345