Ho un'altra implementazione per questo in PHP> = 5.3.0 ed è come un decoratore che Northborn Design ha spiegato.
Tutto ciò di cui abbiamo bisogno è un'API per creare estensioni e un decoratore per applicare le estensioni.
Dobbiamo ricordare che nei metodi di estensione C# non interrompono l'incapsulamento dell'oggetto esteso e non modificano l'oggetto (non serve a tal fine, implementare il metodo sarebbe più efficace). E i metodi estensioni sono puri statiche e ricevono l'istanza dell'oggetto, come nell'esempio che segue (C#, from MSDN):
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
Naturalmente non abbiamo oggetti String in PHP, ma per tutti gli altri oggetti possiamo creare decoratori generici per quel tipo di magia.
permette di vedere la mia applicazione per questo:
L'API:
<?php
namespace Pattern\Extension;
/**
* API for extension methods in PHP (like C#).
*/
class Extension
{
/**
* Apply extension to an instance.
*
* @param object $instance
* @return \Pattern\Extension\ExtensionWrapper
*/
public function __invoke($instance)
{
return Extension::apply($instance);
}
/**
* Apply extension to an instance.
*
* @param object $instance
* @return \Pattern\Extension\ExtensionWrapper
*/
public static function apply($instance)
{
return new ExtensionWrapper($instance, \get_called_class());
}
/**
* @param mixed $instance
* @return boolean
*/
public static function isExtensible($instance)
{
return ($instance instanceof Extensible);
}
}
?>
il decoratore:
<?php
namespace Pattern\Extension;
/**
* Demarcate decorators that resolve the extension.
*/
interface Extensible
{
/**
* Verify the instance of the holded object.
*
* @param string $className
* @return bool true if the instance is of the type $className, false otherwise.
*/
public function holdsInstanceOf($className);
/**
* Returns the wrapped object.
* If the wrapped object is a Extensible the returns the unwrap of it and so on.
*
* @return mixed
*/
public function unwrap();
/**
* Magic method for the extension methods.
*
* @param string $name
* @param array $args
* @return mixed
*/
public function __call($name, array $args);
}
?>
E l'implementazione generica:
<?php
namespace Pattern\Extension;
/**
* Generic version for the Extensible Interface.
*/
final class ExtensionWrapper implements Extensible
{
/**
* @var mixed
*/
private $that;
/**
* @var Extension
*/
private $extension;
/**
* @param object $instance
* @param string | Extension $extensionClass
* @throws \InvalidArgumentException
*/
public function __construct($instance, $extensionClass)
{
if (!\is_object($instance)) {
throw new \InvalidArgumentException('ExtensionWrapper works only with objects.');
}
$this->that = $instance;
$this->extension = $extensionClass;
}
/**
* {@inheritDoc}
* @see \Pattern\Extension\Extensible::__call()
*/
public function __call($name, array $args)
{
$call = null;
if (\method_exists($this->extension, '_'.$name)) {
// this is for abstract default interface implementation
\array_unshift($args, $this->unwrap());
$call = array($this->extension, '_'.$name);
} elseif (\method_exists($this->extension, $name)) {
// this is for real implementations
\array_unshift($args, $this->unwrap());
$call = array($this->extension, $name);
} else {
// this is for real call on object
$call = array($this->that, $name);
}
return \call_user_func_array($call, $args);
}
/**
* {@inheritDoc}
* @see \Pattern\Extension\Extensible::unwrap()
*/
public function unwrap()
{
return (Extension::isExtensible($this->that) ? $this->that->unwrap() : $this->that);
}
/**
* {@inheritDoc}
* @see \Pattern\Extension\Extensible::holdsInstanceof()
*/
public function holdsInstanceOf($className)
{
return \is_a($this->unwrap(), $className);
}
}
?>
L'uso:
assumere una classe di terze parti esiste:
class ThirdPartyHello
{
public function sayHello()
{
return "Hello";
}
}
Crea proprio interno:
use Pattern\Extension\Extension;
class HelloWorldExtension extends Extension
{
public static function sayHelloWorld(ThirdPartyHello $that)
{
return $that->sayHello().' World!';
}
}
Plus: Per interfaccia Lovers, creare un'estensione Abstract:
<?php
interface HelloInterfaceExtension
{
public function sayHelloFromInterface();
}
?>
<?php
use Pattern\Extension\Extension;
abstract class AbstractHelloExtension extends Extension implements HelloInterfaceExtension
{
public static function _sayHelloFromInterface(ThirdPartyOrLegacyClass $that)
{
return $that->sayHello(). ' from Hello Interface';
}
}
?>
Poi usarlo :
////////////////////////////
// You can hide this snippet in a Dependency Injection method
$thatClass = new ThirdPartyHello();
/** @var ThirdPartyHello|HelloWorldExtension $extension */
$extension = HelloWorldExtension::apply($thatClass);
//////////////////////////////////////////
$extension->sayHello(); // returns 'Hello'
$extension->sayHelloWorld(); // returns 'Hello World!'
//////////////////////////////////////////
// Abstract extension
$thatClass = new ThirdPartyHello();
/** @var ThirdPartyHello|HelloInterfaceExtension $extension */
$extension = AbstractHelloExtension::apply($instance);
$extension->sayHello(); // returns 'Hello'
$extension->sayHelloFromInterface(); // returns 'Hello from Hello Interface'
Pro: modo
- molto simili di C# i metodi di estensione in PHP;
- Impossibile testare direttamente un'istanza di estensione come un'istanza dell'oggetto esteso, ma ciò è utile perché è più sicuro perché possiamo avere luoghi in cui l'istanza di tale classe non è estesa;
- Come l'intento di un framework per migliorare l'agilità del team, è necessario scrivere meno;
- L'uso dell'estensione sembra essere parte dell'oggetto ma è appena decorato (forse è interessante per i team sviluppare rapidamente ma rivedere in futuro l'implementazione di tale oggetto esteso se è coinvolta la legacy);
- È possibile utilizzare il metodo statico dell'estensione direttamente per migliorare le prestazioni, ma facendo in modo che si perda la capacità di deridere parti del codice (DI è altamente indicato).
Contro:
- L'estensione devono essere dichiarate per l'oggetto, la sua non è solo come importazione come in C#, è necessario "decorare" l'istanza desiderata per dare l'estensione ad esso.
- Impossibile testare direttamente un'istanza di estensione come un'istanza dell'oggetto esteso, più codice per testare utilizzando l'API;
- Svantaggi delle prestazioni a causa dell'uso di metodi magici (ma quando sono necessarie prestazioni cambiamo linguaggio, ricreamo il nucleo, utilizzo di strutture minimaliste, assemblatore se necessario);
Ecco un Gist per quella Api: https://gist.github.com/tennaito/9ab4331a4b837f836ccdee78ba58dff8
stringhe non sono oggetti in PHP. –
Mi dispiace, che cos'è un "metodo di estensione"? come si differenzia dagli altri metodi? – Artefacto
@Artefacto: "ciao" .MyExtention ("altro"); 'non lo ha spiegato? – caesay