Creazionali
Strutturali
Comportamentali
When we talk about abstract classes we are defining characteristics of an object type, specifying what an object is but in the case of an interface we define a capability and we bond to provide that capability, we are talking about establishing a contract about what the object can do.
An abstract class can have shared state or functionality. An interface is only a promise to provide the state or functionality. A good abstract class will reduce the amount of code that has to be rewritten because it's functionality or state can be shared. The interface has no defined information to be shared.
Use abstract clases and inheritance if you can make the statement "A is a B". Use interfaces if you can make the statement "A is capable of [doing] as", ma usare un 'interfaccia se è chiaro che l'abilità che si vuole esporre appartiene a molti oggetti non collegati
/*
usage:
$myExample =& singleton('Example');
$myExample1 =& singleton('Example1');
$myExample->test = 'just a test';
$myExample1->test = 'another test';
echo 'Example $test: '.$myExample->getTest(); echo '<br>'; echo 'Example1 $test: '.$myExample1->getTest();
*/
function &GetSingleton($class) {
static $instances;
if (!is_array($instances)) {
$instances = array();
}
if (!isset($instances[$class])) {
$instances[$class] = new $class;
}
return $instances[$class];
}
class Singleton{
/*
whenever a PHP program creates an object belonging to the class,
a unique instance will be available within its execution.
*/
/* static */ function &GetInstance(){
static $instance;
if(!isset($instance)){
$object= get_class($this);
$instance = new $object;
}
return $instance;
}
}
function &Factory($class){
// where to look for config params
$config = (isset($GLOBALS['configuration'][$class]) ) ? $GLOBALS['configuration'][$class] : array();
if (!isset($instances[$class])) {
$instance = new $class($config);
}
return $instance;
}
class Factory{
/* static */ function &Create($type, $params=array()){
if(!class_exists($type) || !is_array($params)){
trigger_error('Invalid method parameters');
}
// return a new element object
$tmp = new $type($params);
return $tmp;
}
}
L'obiettivo di questa architettura è isolare un algoritmo all'interno di un oggetto. Il pattern Strategy è utile in quelle situazioni dove sia necessario modificare dinamicamente gli algoritmi utilizzati da un'applicazione.
/*
uso:
function __construct($strategy){
// nel costruttore, inizializza la configurazione
$this->strategyRegistry = array( 'db'=>array('mysql'=>'DB_Mysql') );
//crea la strategia
$this->createStrategy( $field, $trategy );
}
// nei discendenti c possibile usare
function validateData($inputData){
if(!$this->strategy->myFunct() ){
return 'Input data is not valid!';
} else {
return 'Input data is OK!';
}
} */
class Strategy {
var $strategy=NULL;
// array( 'propertyName'=>array('strategyName'=>'implementationClassName') );
// array( 'db'=>array('mysql'=>'DB_Mysql') );
var $strategyRegistry = array();
function createStrategy($field, $strategy){
// controlla che la strategia disponibile sia
if( $this->StrategyIsAvailable($strategy) ){
$c = $this->strategyRegistry[$field][$strategy];
$this->$field = new $c( /* params? */ );
} else {
// ?
}
}
}
sistemi diferenti permettono operazioni simili, ad esempio database o fs.
class Car{ function run(){} }
class StationWagon extends Car{ function run(){ while(true) $this->distance+=1; } }
class Suv extends Car{ function run(){ while(true) $this->distance+=2; } }
class journey{
var $car;
function __construct(& $car) {
$this->car =& $car;
$this->car->run();
}
}
// definisce un algoritmo standard da cui possono essere derivare implementazioni specifiche
class coffeRecipeTemplate {
function __construct(){
// si definisce l'algoritmo generale astratto
if( $this->boilWater() ) {
$this->addCoffe();
}
}
function boilWater(){
// questo metodo va ridefinito nelle sottoclassi
}
function addCoffe(){}
}
class MyCoffeRecipe extends coffeRecipeTemplate {
// si implementa l'azione corretta per il tipo di oggetto
function boilWater(){ while($this->temp < 100 ){ $this->boil(); } }
function addCoffe(){ while($this->qta < 10 ){ $this->add(); } }
}
// definisce un oggetto operazione, assegna le operazioni a una lista, esegue le operazioni nella lista
interface ICommand{
public funcion execute();
}
class Commnad1 implements ICommand{}
class Commnad2 implements ICommand{}
class commander{
function __constructor(){
// i command possono essere caricati a runtime o seguendo logiche opportune
$this->commands = array( new Command1(), new Command2() );
}
// esegue i comandi per aggregare un output complesso
function operate(){
// l'output può essere di un tipo qualsiasi, dipende dalla logica corrente
$output = '';
foreach( $this->command as $c ){
$output .= $c->execute();
}
return $output;
}
}
"let's rewrite this function so it obtains all its state from the caller"
There are two variations of a registry: There is the global registry (Which is far the most common, and which this is an example of). And there is a local registry. A local registry is passed to objects that need it, rather than obtained through a global symbol (static class, singleton etc.). A local registry has a lower degree of coupling, but is also slightly more abstract, so there is a tradeoff there.
You can also go even further and use full dependency injection, where you explicitly pass all dependency to the objects that needs them. This can be a bit tedious in larger applications. You can couple this with a dependency injection container, which is a piece of code that "knows" which dependencies which classes have. This is even more complex than a local registry, but has a very low degree of coupling.
Constructor injection ensures all of the dependencies are stated up front. The downside with constructor injection is that the configuration of your object graph is more complicated. This is mitigated through the use of an IoC container.
// we have many different types of cache!
interface ICache {}
class FileCache implements ICache {}
class MemoryCache implements ICache {}
class DBCache implements ICache {}
class NullCache implements ICache {} // the easy to test one!
// use adapters to reuse someone else code with your rules and conventions!
class CacheAdapter extends ThirdPartyCache implements ICache {}
class FeedFetcher {
protected $cache;
// all "dependency" are "injected" from outside, not constructed inside the class, so that's the "inversion of control"
function __construct(ICache $cache) {
$this->cache = $cache;
}
}
alternatively, the Locator
class FeedFetcher {
function __construct() {
$this->cache = Locator::get('cache');
}
}
Locator::set('cache', new FileCache("/tmp"));
$fetcher = new FeedFetcher();
twittee DIC, similar to Pimple but stripped to the bare minimum
class Container {
protected $s=array();
function __set($k, $c) { $this->s[$k]=$c; }
// allways pass the container itself to the anonimous functions that builds dependencies!
function __get($k) { return $this->s[$k]($this); }
}
// shows how to create a Zend_Mail object that sends its emails using a Gmail account
$c = new Container();
// parameters, so that's still possible to configure services at runtime
$c->mailer_class = function () { return 'Zend_Mail'; };
$c->mailer_username = function () { return 'test'; };
$c->mailer_password = function () { return 'test'; };
// objects / services
$c->mailer_transport = function ($c) {
return new Zend_Mail_Transport_Smtp(
'smtp.gmail.com',
array(
'auth' => 'login',
'username' => $c->mailer_username,
'password' => $c->mailer_password,
'ssl' => 'ssl',
'port' => 465,
)
);
};
// $c is the container itself, where configurations are stored
$c->mailer = function ($c) {
$obj = new $c->mailer_class();
$obj->setDefaultTransport($c->mailer_transport);
return $obj;
};
// get the mailer **configured** by the Container
$mailer = $c->mailer;
print array_map(
function($x){return $x + 1;},
array(1, 2, 3, 4, 5)
);
print array_filter(
array(1, 2, 3, 4, 5),
function($x){ return $x % 2 == 0;}
);
print array_reduce(
array(1, 2, 3, 4, 5),
function($x, $y){ return $x + $y;}
);