Si bel pippone di spiegazione, ma il discorso è che non è che perchè voglio un oggetto "immutabile" allora devo usare quella sottospecie di singleton e istanziare ogni volta ogni oggetto con una factory... di Value Object "con gli stessi valori" ne puoi avere seimila, non stà nel pattern impedire che due value object abbiano gli stessi valori, dice solo che il value object deve essere immutabile e che due value object sono uguali se uguali i valori. Il discorso se istanziare o meno N volte un value object dipende da 1) che cosa rappresenta questo value object e 2) da quanti effettivamente ne stai istanziando e 3) ma quante volte vorrai utilizzarlo un value object? in un app web poi... quante ne istanzierai? 1? 2? 3?
Cmq il mio intervento era semplicemente per dire che la tua implementazione risolve delle specifiche ben precise (non ripetere due volte lo stesso value object) e impone che lo stesso due oggetti che devono usare una istanza del value object puntano entrambi alla stessa istanza, cosa nello specifico inutile e che potrebbe anche risultare controproducente in alcuni casi. Tutto quà... lungi da me l'idea di darti del cretino (cosa che per altro tu hai fatto tranquillamente), dicevo solo che l'implementazione (alias: tutto quel casino... alias alias: tutto quel popò di roba ... alias alias alias: tutte quelle righe di codice) andava secondo me fuori dagli scopi del pattern e che era del tutto opinabile (cosa per altro detta anche da te medesimo). punto.
IP-PBX management: http://www.easypbx.it
Old account: 2126 messages
Oldest account: 3559 messages
Non ti darei mai del cretino, a volte però sei un po' frettoloso nel sentenziare.
Tu scrivi:.il value object deve essere immutabile e che due value object sono uguali se uguali i valori
Esattamente quello che fa la mia classe, cercando di non allocare memoria in più. Oviamente con un piccolo overhead d'esecuzione, ma si sa, più sei veloce in esecuzione e più memoria usi, meno memoria usi e meno sei veloce in esecuzione. Quanti ne alloco in un'applicazione web? Non lo so, la sorte sorride a chi si prepara. Infine non mi sembra di aver scritto così tanto codice.
Il resto è lo script di test e l'adattamento della classe d'esempio specifica di Al_katraz, ma la classe può essere utilizzata così com'è.Codice PHP:<?php
class OggettoCostante {
private static $gliOggetti = [];
private $iDati = [];
public static function creaNuovoOggetto(array $valori) {
$hash = hash("sha256", serialize($valori)); //tengo le dita incrociate
if (!isset(self::$gliOggetti[$hash]))
self::$gliOggetti[$hash] = new OggettoCostante($valori);
return self::$gliOggetti[$hash];
}
private function __construct(array $valori) {
foreach ($valori as $nomeProprieta => $valore) {
if (is_object($valore))
$valore = clone $valore;
$this->iDati[$nomeProprieta] = $valore;
}
}
public function __get($name) {
if (isset($this->iDati[$name]))
return $this->iDati[$name];
throw new Exception("La proprieta non e' definita");
}
public function __set($name, $value) {
throw new Exception("Non e' ammessa la modifica dello stato dell'oggetto");
;
}
}
?>
Se poi non ti piace il fatto che restituisca la stessa istanza a parità di stato, posso strappare via quella parte e rendere pubblico il costruttore. Così ci saranno i soli 3 metodi __construct __get e __set.
Ti piace di più?Codice PHP:<?php
class OggettoCostante {
private $iDati = [];
public function __construct(array $valori) {
foreach ($valori as $nomeProprieta => $valore) {
if (is_object($valore))
$valore = clone $valore;
$this->iDati[$nomeProprieta] = $valore;
}
}
public function __get($name) {
if (isset($this->iDati[$name]))
return $this->iDati[$name];
throw new Exception("La proprieta non e' definita");
}
public function __set($name, $value) {
throw new Exception("Non e' ammessa la modifica dello stato dell'oggetto");
;
}
}
?>
Santino, ti voglio bene (detto sinceramente)! E non ti prendo in giro quando dico che so che ne sai a pacchi.
![]()
Siamo sempre troppo gelosi delle nostre grandi piccole opere! - Grino inedito.
Lavori e Lavoretti
In questa seconda parte andiamo ad aggiungere un metodo alla classe url che servirà ad "appendere" dei parametri alla querystring. A causa della natura immutabile della classe url, il metodo ritornerà una nuova classe url con i parametri aggiunti alla querystring, dimostrando di fatto l'immutabilità.
Alcuni classici esempi di "value object" che si usano spesso nelle applicazioni sono: date, numeri di telefono, indirizzi email ect ect.. La struttura degli oggetti, ovviamente, può cambiare in base al contesto di utilizzo e possiamo avere anche altri tipi di "value object" oltre a quelli menzionati, questo dipende sempre dalle necessità del progetto.
Ad esempio:
un indirizzo email è composto da parti che dovranno essere attinenti alle specifiche RFC 5321 e RFC 5322
Purtroppo a differenza di altri programmi tipo C# e Java, PHP non supporta nativamente i "value object" immutabili ma adesso vediamo quanto sia semplice applicare il pattern.
Siamo partiti con una classe chiusa alla quale abbiamo aggiunto i metodi getter per accedere alle proprietà. Ora per mantenere l'immutabilità della classe (che ci proibisce di modificare il nostro oggetto una volta popolato) realizziamo il metodo addQuerysting()
Niente di fantascientificoCodice PHP:public function addQuerystring( array $params ) {
if ( empty( $params ) ) {
throw new Exception( 'I parametri non sono validi' );
}
$querystring = '';
foreach ( $params AS $key=>$value ) {
$querystring .= '&' . $key . '=' . urlencode( $value );
}
$querystring = ( $this->querystring === NULL )
? trim( $querystring, '&' )
: $this->querystring . $querystring;
$url = $this->scheme . '://' . $this->host . $this->path . '?' . $querystring;
return new url( $url );
}
il nostro metodo sfrutta un array associativo con i nuovi parametri da aggiungere alla querystring. L'aspetto importante da notare è che, invece di modificare la querystring esistente, ne creiamo una nuova e la usiamo per creare un nuovo oggetto url (una specie di factory ma ne parleremo più avanti).
In questo modo abbiamo dimostrato la natura immutabile del nostro "value object" visto che ogni tentativo di modificare i valori assegnati da come risultato un nuovo "value object" dello stesso tipo, preservandone l'immutabilità.
Questa volta, più che un voto.. è favoreggiamento.
Molto bene ora abbiamo completato la classe, ora possiamo fare i test:
url.class.php
vediamo un piccolo esempio che dimostra la natura immutabile del "value object"Codice PHP:final class url {
private $scheme;
private $host;
private $path;
private $querystring;
public function __construct( $url ) {
if ( !filter_var( $url, FILTER_VALIDATE_URL, array( FILTER_FLAG_SCHEME_REQUIRED, FILTER_FLAG_HOST_REQUIRED ) ) ) {
throw new Exception( "L'url he hai specificato non è valido!" );
}
$url = parse_url( $url );
$this->scheme = $url['scheme'];
$this->host = $url['host'];
if ( isset( $url['path'] ) ) {
$this->path = $url['path'];
}
if ( isset( $url['query'] ) ) {
$this->queryString = $url['query'];
}
}
public function getScheme() {
return $this->scheme;
}
public function getHost() {
return $this->host;
}
public function getPath() {
return $this->path;
}
public function getQuerystring() {
return $this->querystring;
}
public function addQuerystring( array $params ) {
if ( empty( $params ) ) {
throw new Exception( 'I parametri non sono validi' );
}
$querystring = '';
foreach ( $params AS $key=>$value ) {
$querystring .= '&' . $key . '=' . urlencode( $value );
}
$querystring = ( $this->querystring === NULL )
? trim( $querystring, '&' )
: $this->querystring . $querystring;
$url = $this->scheme . '://' . $this->host . $this->path . '?' . $querystring;
return new url( $url );
}
}
Bene per oggi ho finito, nella prossima parte proviamo a sfruttare i "value object" in altri modi più complessi.Codice PHP:require_once( 'url.class.php' );
$url = new url( 'http://www.html.it' );
//aggiungiamo alcuni parametri
$nuovoUrl = $url->addQuerystring( array( 'uid'=>1, 'name'=>'Paolo' ) );
if ( $url == $nuovoUrl ) {
echo 'I due "value URL object" sono uguali';
} else {
echo 'I due "value URL object" NON sono uguali';
}
//output: I due "value URL object" NON sono uguali
Spero sia tutto chiaro, ora via con i commenti.
Ringrazio tutti quelli che stanno contribuendo al topic
Andrea
Questa volta, più che un voto.. è favoreggiamento.
Se posso, queste sono le mie due versioni alternative di cui una alloca sempre spazio e l'altra cerca di risparmiarlo permettendo l'accesso alle proprietà tramite operatore ->. La classe URL costa di due soli metodi e non ha bisogno di metodi d'accesso. L'accesso in scrittura è ovviamente negato.
Versione spendacciona:
Versione che non istanzia nuovi oggetti a fronte del ripresetnarsi di stati già utilizzati:Codice PHP:<?php
class OggettoCostante {
private $iDati = [];
public function __construct(array $valori) {
foreach ($valori as $nomeProprieta => $valore) {
if (is_object($valore))
$valore = clone $valore;
$this->iDati[$nomeProprieta] = $valore;
}
}
public function __get($name) {
if (isset($this->iDati[$name]))
return $this->iDati[$name];
throw new Exception("La proprieta non e' definita");
}
public function __set($name, $value) {
throw new Exception("Non e' ammessa la modifica dello stato dell'oggetto");
}
}
final class URL extends OggettoCostante {
public function __construct(array $url) {
if (!isset($url['url']) or !filter_var($url['url'], FILTER_VALIDATE_URL, array(FILTER_FLAG_SCHEME_REQUIRED, FILTER_FLAG_HOST_REQUIRED))) {
throw new Exception("Parametro non correttamente formattato o url non valido.");
}
$purl = parse_url($url['url']);
$scheme = $purl['scheme'];
$host = $purl['host'];
$path = (isset($purl['path'])) ? $purl['path'] : '';
$query = (isset($purl['query'])) ? $purl['query'] : '/';
parent::__construct(array('url' => $url['url'], 'scheme' => $scheme, 'host' => $host, 'path' => $path, 'query' => $query));
}
public function addQuery(array $params) {
if (empty($params))
throw new Exception('I parametri non sono validi');
$querystring = '';
foreach ($params as $key => $value)
$querystring .= '&' . $key . '=' . urlencode($value);
$querystring = (empty($this->query)) ? trim($querystring, '&') : $this->query . $querystring;
$url = $this->scheme . '://' . $this->host . $this->path . '?' . $querystring;
return new URL(['url' => $url]);
}
}
$url = new URL(['url' => 'h*t*t*p://www.html.it']);
//aggiungiamo alcuni parametri
$nuovoUrl = $url->addQuery(array('uid' => 1, 'name' => 'Paolo'));
if ($url == $nuovoUrl)
echo 'I due "value URL object" sono uguali';
else
echo 'I due "value URL object" NON sono uguali';
//output: I due "value URL object" NON sono uguali
?>
Inutile dire che l'output prodotto è quello atteso. Non capisco perchè non posso postre indirizzi web nel codice e mi tocca aggiungere gli *Codice PHP:<?php
class OggettoCostante {
private static $gliOggetti = [];
private $iDati = [];
/**
* Crea un nuovo oggetto costante. In $valori vanno specificate coppie
* [nomeProprietà => valore, ...]. Le proprietà saranno poi accessibili
* tramite la sintassi $oggetto->nomeProprietà in sola lettaura
* @param array $valori
* @return type
*/
public static function creaNuovoOggetto(array $valori) {
$hash = hash("sha256", serialize($valori)); //tengo le dita incrociate
$class = get_called_class();
if (!isset(self::$gliOggetti[$class][$hash]))
self::$gliOggetti[$class][$hash] = static::istanziaOggetto($valori);
return self::$gliOggetti[$class][$hash];
}
protected static function istanziaOggetto(array $valori){
return new OggettoCostante($valori);
}
protected function __construct(array $valori) {
foreach ($valori as $nomeProprieta => $valore) {
if (is_object($valore))
$valore = clone $valore;
$this->iDati[$nomeProprieta] = $valore;
}
}
public function __get($name) {
if (isset($this->iDati[$name]))
return $this->iDati[$name];
throw new Exception("La proprieta non e' definita");
}
public function __set($name, $value) {
throw new Exception("Non e' ammessa la modifica dello stato dell'oggetto");
}
}
final class URL extends OggettoCostante {
/**
* Crea un nuovo oggetto url costante. Il parametro è un array del tipo:
* ['url' => 'http://www.google.com']
* @param array $url
* @throws Exception
*/
public static function creaNuovoOggetto(array $url) {
if (!isset($url['url']) or !filter_var($url['url'], FILTER_VALIDATE_URL, array(FILTER_FLAG_SCHEME_REQUIRED, FILTER_FLAG_HOST_REQUIRED))) {
throw new Exception("Parametro non correttamente formattato o url non valido.");
}
$purl = parse_url($url['url']);
$scheme = $purl['scheme'];
$host = $purl['host'];
$path = (isset($purl['path'])) ? $purl['path'] : '/';
$query = (isset($purl['query'])) ? $purl['query'] : '';
return parent::creaNuovoOggetto(array('url' => $url['url'], 'scheme' => $scheme, 'host' => $host, 'path' => $path, 'query' => $query));
}
protected static function istanziaOggetto(array $valori) {
return new URL($valori);
}
public function addQuery( array $params ) {
if ( empty( $params ) )
throw new Exception( 'I parametri non sono validi' );
$querystring = '';
foreach ( $params as $key=>$value )
$querystring .= '&' . $key . '=' . urlencode( $value );
$querystring = (empty($this->query))? trim($querystring, '&'):$this->query . $querystring;
$url = $this->scheme . '://' . $this->host . $this->path . '?' . $querystring;
return self::creaNuovoOggetto(['url' => $url]);
}
}
$url = URL::creaNuovoOggetto( ['url' => 'h*t*t*p://www.html.it']);
//aggiungiamo alcuni parametri
$nuovoUrl = $url->addQuery( array( 'uid'=>1, 'name'=>'Paolo' ) );
if ( $url == $nuovoUrl ) {
echo 'I due "value URL object" sono uguali';
} else {
echo 'I due "value URL object" NON sono uguali';
}
//output: I due "value URL object" NON sono uguali
?>
![]()
Siamo sempre troppo gelosi delle nostre grandi piccole opere! - Grino inedito.
Lavori e Lavoretti
Ok mi sono riletto il codice e ti posto le mie impressioni
Primo su tutte tendo sempre ad evitare pattern singleton e metodi statici.. Li vedo troppo inconsistenti in un approccio OOP, mi sanno troppo da vecchio "global" che non si capisce mai che attinenza hanno le classi..
A livello di codice ovviamente il tuo approccio è comunque valido anche se lo vedo un po' troppo articolato.. Un "value object" è un semplice contenitore di informazioni e le informazioni contenute sono diverse da oggetto a oggetto. Vedo molto più semplice definire singolarmente i vari oggetti.
Vedo anche che ti preoccupi dei tempi di esecuzione, con le nuove versioni di php ci sono le Closures che aprono nuovi mondi sui tempi di caricamento ed esecuzione del codice ma è un argomento che esula dal contesto che stiamo analizzando e credo troppo complesso per ora. Ne parlerò un po' più avanti
Preparati che nella prossima parte ti complico la vita, vediamo come te la cavi
grazie intanto
![]()
Ultima modifica di Al_katraz984; 22-12-2013 a 01:35
Questa volta, più che un voto.. è favoreggiamento.
Eccoci di nuovo,
in questa terza parte vi porto un altro esempio di ValueObject, cercheremo di costruire una classe immutabile per creare widget HTML. Qualcosa di più pratico e utile
Spero di essere stato chiaro fino ad ora e semplice..
Ok, partiamo
lo scopo è quello di realizzare un sistema per la creazione di "div" html, iniziamo subito a definire un contratto comune a tutti i widget usando un'interfaccia
interface.php
in questo modo tutti gli oggetti che implementano l'interfaccia htmlWidgetInterface dovranno fornire un'implementazione del metodo render().Codice PHP:interface htmlWidgetInterface {
public function render();
}
Il prossimo passo sarà quello di scrivere una classe in grado di creare i nostri "div"
div.class.php
La classe è molto semplice e facile da capire, passiamo il contenuto al nostro costruttore e con il metodo render() visualizziamo a video il div mantenendo l'immutabilià dell'oggetto.Codice PHP:final class div implements htmlWidgetInterface {
private $content;
public function __construct( $content ) {
if ( !is_string( $content ) || empty( $content ) ) {
throw new Exception( 'Il contenuto non è valido' );
}
$this->content = $content;
}
public function getContent() {
return $this->content;
}
public function nest( div $div ) {
$content = $this->render() . $div->render();
return new div( $content );
}
// implementazione dell'interfaccia
public function render() {
return '<div>' . $this->content . '</div>';
}
}
Soffermiamoci sul metodo nest().
Questo metodo ci permette (grazie al type-hinting) di avere la sicurezza che vengano passati solamente oggetti del tipo "div" garantendoci la presenza del metodo render() in quanto sappiamo che la classe div deve avere da contratto un metodo render.
Proviamo a dimostrare la natura immutabile della nostra classe:
That's it!Codice PHP:require_once( 'interface.php' );
require_once( 'div.class.php' );
$div1 = new div( 'Contenuto del primo div.' );
$div2 = new div( 'Contenuto del secondo div.' );
$div3 = $div1->nest( $div2 );
echo $div3->render();
//output:
// <div><div>Contenuto del primo div.</div><div>Contenuto del secondo div.</div></div>
spero sia chiaro e semplice.
Per chi si vuole esercitare potete codificare i widget mancanti, ci sono cosi tanti tags html....
Credo che per quanto riguarda i value object ci siano sufficenti esempi per comprenderne la logica e i concetti. Se qualcuno vuole dire la sua e portare qualche altro esempio ben venga, se ci sono domande fatele, io intanto preparo il prossimo pattern
![]()
Questa volta, più che un voto.. è favoreggiamento.
Le feste son feste e quindi con ritardo apporto il mio contributo basato sulla solita precedente teoria che hai ritenuto valida anche se troppo articola e non di tuo gradimento.
Mi cheidevo per l'esempio specifico che hai presentato perchè inventare una funzione render(), considerato che stai trasformato l'oggetto nella sua rappresentazione stringa tipica per una echo. Quindi perchè non utilizzare __toString(). In più il div è solo uno dei possibili componenti HTML. Non sarebbe meglio se l'objectValue fosse inteso come di tipo htmlWidgetInterface. In questo modo la nest non deve accettare per forza una classe div come argomento, ma può lavorare su una platea di classi (tutte quelle che implementano l'interfaccia). In tal senso il metodo nest rientra nell'interfaccia htmlWidgetInterface. Certo può sembrare di muoversi al di fuori dal pattern, d'altra parte un oggetto che istanzia il tuo div è sia di tipo div che di tipo htmlWidgetInterface, quindi la ritengo cosa ammissibile.
Codice che istanzia sempre oggetti costanti diversi.
La classe cOggettoCostante che permette di creare degli oggetti immutabili generici:
Come giustamente dici, stabiliamo l'interfaccia di un componente html. Ho cambiato la tua nest in unisci perchè non mi sembra un vero annidamento. In pratica è creato un nuovo oggetto dello stesso tipo dell'oggetto che invoca il metodo e il cui contenuto è la concatenazione dell'oggetto chiamante e dell'oggetto passato come argomento (comunque sempre un oggetto iComponenteHTML). La funzione unisci lavora in modo stretto su oggetti di tipo iComponenteHTML divendo più formale e restituisce un iComponenteHTML. Sarà così possbile ampliare la platea delle classi trattate come oggetti immutabili a tutte quelle classi del mondo HTML che si vorrà definire:Codice PHP:class cOggettoCostante {
private $iDati = [];
public function __construct(array $valori) {
foreach ($valori as $nomeProprieta => $valore) {
if (is_object($valore))
$valore = clone $valore;
$this->iDati[$nomeProprieta] = $valore;
}
}
public function __get($name) {
if (isset($this->iDati[$name]))
return $this->iDati[$name];
throw new Exception("La proprieta non e' definita");
}
public function __set($name, $value) {
throw new Exception("Non e' ammessa la modifica dello stato dell'oggetto");
}
}
Adesso le classi che gestiscono la produzione HTML. Il div lo ritengo un elemento complesso per poter essere definito in modo diretto. C'è da tenere conto che nella pagina le stringhe di testo vanno codificate con le relative entità HTML senza interferire con i tag di apretura e chiusura degli altri elementi. Così come vanno codificate le stringhe che sono attributo dei tag. Ho quindi strutturato un abbozzo di famiglia di classi, potenzialmente espandibile con altri tag e proprietà per gli elementi class, id, ecc, così organizzata così:Codice PHP:interface iComponenteHTML {
public function __toString();
public function unisci(\iComponenteHTML $contenuto);
}
codice:cOggettoCostante | |-aContenutoBody implementa iComponenteHTML | |-cStringaHTML | |-cTag | |-cDivA questo punto il codice d'esempio che è:Codice PHP:abstract class aContenutoBody extends cOggettoCostante implements iComponenteHTML {
const contenuto = 'contenuto';
public function __toString() {
return $this->contenuto;
}
abstract public function unisci(\iComponenteHTML $contenuto);
protected static function verificaContenutoAccettabile($contenuto) {
return is_string($contenuto) or is_a($contenuto, 'iComponenteHTML');
}
}
class cStringaHTML extends aContenutoBody {
public function __construct(array $valori) {
if (isset($valori[self::contenuto]) and is_string($valori[self::contenuto]))
parent::__construct([self::contenuto => htmlspecialchars($valori[self::contenuto])]);
else
throw new Exception("Una stringa HTML deve essere una stringa di testo o contenuto non definito");
}
public function unisci(\iComponenteHTML $contenuto) {
return new cStringa([self::contenuto => $this . $contenuto]);
}
}
class cTag extends aContenutoBody {
const tag = 'tag';
public function __construct(array $valori) {
if (isset($valori[self::contenuto]) and isset($valori[self::tag]) and self::verificaContenutoAccettabile($valori[self::contenuto]))
parent::__construct([self::tag => $valori[self::tag], self::contenuto => $valori[self::contenuto]]);
else
throw new Exception("Occorre definire la proprietà contenuto e tag");
}
public function unisci(\iComponenteHTML $contenuto) {
return new cTag([self::tag => $this->tag, self::contenuto => $this . $contenuto]);
}
public function __toString() {
return "<$this->tag>" . parent::__toString() . "</$this->tag>";
}
}
class cDiv extends cTag {
public function __construct(array $valori) {
$valori[self::tag] = 'div';
parent::__construct($valori);
}
}
Tutti e 3 gli oggetti istanziati non possono essere modificati poichè figli di cOggettoCostante.Codice PHP:$div1 = new cDiv([cDiv::contenuto => new cStringaHTML([cStringaHTML::contenuto => 'Contenuto del primo <div>.'])]);
$div2 = new cDiv([cDiv::contenuto => new cStringaHTML([cStringaHTML::contenuto => 'Contenuto del secondo <div>.'])]);
$div3 = $div1->unisci($div2);
echo $div3;
La versione che non alloca più istanze per oggetti costanti con stato identico
A questo punto per completezza ti posto anche la versione che fa uso della classe cOggettoCostante che istanzia un nuovo oggetto solo se lo stato per la classe richiesta non è mai stato utilizzato (quella sparagnina e che ti piace ancora meno). Le classi in gioco sono sempre le stesse di prima, il codice d'altra parte cambia leggermente dovendo le figlie ridefinire i metodi statici crea e istanzia e dovendo il client fare udo dei metodi statici per la creazione degli oggetti:
In entrambi i casi l'output prodotto è:Codice PHP:<?php
class cOggettoCostante {
private static $gliOggetti = [];
private $iDati = [];
public static function crea(array $valori) {
$hash = hash("sha256", serialize($valori)); //tengo le dita incrociate
$class = get_called_class();
if (!isset(self::$gliOggetti[$class][$hash]))
self::$gliOggetti[$class][$hash] = static::istanzia($valori);
return self::$gliOggetti[$class][$hash];
}
protected static function istanzia(array $valori) {
return new OggettoCostante($valori);
}
protected function __construct(array $valori) {
foreach ($valori as $nomeProprieta => $valore) {
if (is_object($valore))
$valore = clone $valore;
$this->iDati[$nomeProprieta] = $valore;
}
}
public function __get($name) {
if (isset($this->iDati[$name]))
return $this->iDati[$name];
throw new Exception("La proprieta non e' definita");
}
public function __set($name, $value) {
throw new Exception("Non e' ammessa la modifica dello stato dell'oggetto");
}
}
interface iComponenteHTML {
public function __toString();
public function unisci(\iComponenteHTML $contenuto);
}
abstract class aContenutoBody extends cOggettoCostante implements iComponenteHTML {
const contenuto = 'contenuto';
public function __toString() {
return $this->contenuto;
}
abstract public function unisci(\iComponenteHTML $contenuto);
protected static function verificaContenutoAccettabile($contenuto) {
return is_string($contenuto) or is_a($contenuto, 'iComponenteHTML');
}
}
class cStringa extends aContenutoBody {
public static function crea(array $valori) {
if (isset($valori[self::contenuto]) and is_string($valori[self::contenuto]))
return parent::crea([self::contenuto => htmlspecialchars($valori[self::contenuto])]);
else
throw new Exception("Occorre definire la proprietà contenuto che sia di tipo stringa");
}
protected static function istanzia(array $valori) {
if (isset($valori[self::contenuto]) and is_string($valori[self::contenuto]))
return new cStringa($valori);
throw new Exception("Una stringa HTML deve essere una stringa di testo o contenuto non definito");
}
public function unisci(\iComponenteHTML $contenuto) {
return self::crea([self::contenuto => $this . $contenuto]);
}
}
class cTag extends aContenutoBody {
const tag = 'tag';
public static function crea(array $valori) {
if (isset($valori[self::contenuto]) and isset($valori[self::tag]) and self::verificaContenutoAccettabile($valori[self::contenuto]))
return parent::crea([self::tag => $valori[self::tag], self::contenuto => $valori[self::contenuto]]);
else
throw new Exception("Occorre definire la proprietà contenuto e tag");
}
protected static function istanzia(array $valori) {
return new cDiv($valori);
}
public function unisci(\iComponenteHTML $contenuto) {
return self::crea([self::tag => $this->tag, self::contenuto => $this . $contenuto]);
}
public function __toString() {
return "<$this->tag>" . parent::__toString() . "</$this->tag>";
}
}
class cDiv extends cTag {
public static function crea(array $valori) {
$valori[self::tag] = 'div';
return parent::crea($valori);
}
protected static function istanzia(array $valori) {
return new cDiv($valori);
}
}
$div1 = cDiv::crea([cDiv::contenuto => cStringa::crea([cStringa::contenuto => 'Contenuto del primo <div>.'])]);
$div2 = cDiv::crea([cDiv::contenuto => cStringa::crea([cStringa::contenuto => 'Contenuto del secondo <div>.'])]);
$div3 = $div1->unisci($div2);
echo $div3;
?>
avente codice HTMLcodice:Contenuto del primo <div>. Contenuto del secondo <div>.
Nota: La classe cOggettoCostante non è completa e dovrebbe definire un metodo uguale(\cOggettoCostante $oggetto) che ci dica se due costanti oggetto definiscono le stesse proprietà con lo stesso valore, oltre ad un metodo proprietaDisponibili() che restituisca l'elenco delle proprietà accessibili in lettura. Sarebbe auspicabile che l'oggetto implementi l'interfaccia arrayAccess in modo che $oggetto->proprieta sia equivalente a $oggetto['proprieta'] semplificando il codice in molti contesti.codice:
<div><div>Contenuto del primo <div>.</div><div>Contenuto del secondo <div>.</div></div>
![]()
Siamo sempre troppo gelosi delle nostre grandi piccole opere! - Grino inedito.
Lavori e Lavoretti