classificazione

Creazionali

  • Abstract factory
  • Builder
  • Factory
  • Prototype
  • Singleton

Strutturali

  • Adapter
  • Bridge
  • Composite
  • Decorator
  • Façade
  • Flyweight
  • Proxy

Comportamentali

  • Chain of responsibility
  • Command
  • Interpreter
  • Iterator
  • Mediator
  • Memento
  • Observer
  • State
  • Strategy
  • Template method
  • Visitor

OOP

Abstract vs Interface

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)){
          
$objectget_class($this);
          
$instance = new $object;
      }
      return 
$instance;
    }
}

Factory

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;
    }
}

Strategy

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 {
       
// ?
     }
   }
}

Adapter

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();
  }
}

Template

// 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(); } }
}

Command

// 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;
  }
}

Registry and Dependency Injection

"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;

Functional

print  array_map(
    function(
$x){return $x 1;},
    array(
12345)
  );

print  
array_filter(
    array(
12345),
    function(
$x){ return $x == 0;}
  );

print 
array_reduce(
  array(
12345),
  function(
$x$y){ return $x $y;}
);