Ho un sistema che consente a determinate funzionalità di essere implementate in modi diversi, ma richiede che la funzionalità di ogni classe di implementazione sia racchiusa in un altro livello. Ad esempio, ciascun implementatore ha un metodo doFoo
che interagisce con un'API. Le classi implementor possono essere caricate in fase di runtime (sono essenzialmente plug-in per l'hooking in API diverse) e quindi l'insieme di possibili implementor non è fisso e non può essere gestito semplicemente da una switch
o da una struttura simile a enum. / p>
Ma ogni volta che una delle classi implementor esegue il suo metodo doFoo
, devo anche assicurarmi che il codice aggiuntivo, specifico del metodo, avvenga prima e dopo (si pensi alla registrazione a livello di sistema, alla gestione degli errori e alla sanità di input e output -check che sono gli stessi per tutti gli implementatori).
Ho la seguente configurazione di base:
- un'interfaccia,
FooInterface
- una classe astratta
FooAbstract
che implementaFooInterface
e imposta alcune azioni predefinite. - una classe concreta
Foo
che estendeFooAbstract
.Foo
serve come una sorta di classe factory / bridge che istanzia un implementatore e aggiunge il proprio codice quando chiama i metodi di un implementatore - classi concrete
FooImplementorABC
,FooImplementorXYZ
, ecc., ognuna delle quali estendeFooAbstract
.
In questo modo, quando un utente desidera utilizzare l'implementazione ABC
di FooInterface
, l'utente chiama new Foo('ABC')
. Il costruttore Foo
crea l'istanza corretta e il codice implementatore come necessario.
Codice di esempio (solo codice di esempio, le classi reali sono più complesse e sono tutte suddivise in file e caricate automaticamente in modo appropriato):
<?php
interface FooInterface
{
public function doFoo();
public function doBar();
public function doGenericThing();
}
abstract class FooAbstract implements FooInterface
{
abstract public function doFoo();
abstract public function doBar();
public function doGenericThing()
{
echo "Doing something generic\n";
}
}
class Foo extends FooAbstract
{
/** @var FooAbstract */
private $implementor;
/**
* The constructor for this class accepts an implementation name.
* It then attempts to instantiate the appropriate implementor
* class.
*
* @param $implementationName String
*/
public function __construct($implementationName)
{
$implementorClassName = 'FooImplementor' . $implementationName;
try {
if (class_exists($implementorClassName)) {
$this->implementor = new $implementorClassName(); // Yes, this works in PHP! One of its best or worst features, depending on your perspective.
} else {
throw new Exception("ERROR: No implementation for $implementationName found!");
}
} catch (Exception $e) {
echo "\n\n{$e->getMessage()}\n\n";
}
}
public function doFoo()
{
echo "Doing Foo\n";
// Do some stuff
$this->implementor->doFoo();
// Do some stuff
echo "Done with Foo\n\n";
}
public function doBar()
{
echo "Doing Bar\n";
// Do some stuff
$this->implementor->doBar();
// Do some stuff
echo "Done with Bar\n\n";
}
}
class FooImplementorABC extends FooAbstract
{
public function doFoo()
{
echo "Foo Implementor ABC just did Foo!\n";
}
public function doBar()
{
echo "Foo Implementor ABC just did Bar!\n";
}
}
class FooImplementorXYZ extends FooAbstract
{
public function doFoo()
{
echo "Foo Implementor XYZ just did Foo!\n";
}
public function doBar()
{
echo "Foo Implementor XYZ just did Bar!\n";
}
}
$myFooABC = new Foo('ABC');
$myFooXYZ = new Foo('XYZ');
$myFooABC->doFoo();
$myFooXYZ->doFoo();
$myFooABC->doBar();
$myFooXYZ->doBar();
$myFooQRS = new Foo('QRS'); // displays an error message
Tutto questo funziona bene; i metodi vengono tutti correttamente incapsulati, i dettagli di implementazione non sono importanti per il consumatore, ecc.
Le mie domande sono:
- È un pattern o un anti-pattern?
- Se è un modello, c'è un nome per questo? Non riesco a capire cosa sarebbe. Mi sembra un ibrido tra fabbrica, ponte e motivi di decorazione.
- Se si tratta di un anti-pattern, perché? Come mi morderà per la strada?