Restringendo i tipi di oggetti che possono essere aggiunti tra loro usando il Pattern composito

0

La situazione : sto costruendo un framework di applicazioni PHP personalizzate. Ho implementato un pattern composito in modo da poter costruire un albero di oggetti che rappresenta la pagina da renderizzare. Esempio:

abstract class \Block\BlockAbstract {
     protected _blocks = array();
     public function __construct($properties);
     public function getBlocks();
     public function addBlock($block);

     ...

     public function render();

}

Tutti i miei "blocchi" oggetto ereditano da questa classe astratta che sta funzionando alla grande e il rendering è stato un gioco da ragazzi con l'aiuto di una piccola ricorsione.

Il problema : Ho bisogno di un modo per convalidare quali classi di blocchi possono essere aggiunte ad altri blocchi. Ad esempio, ho 4 tipi di blocchi distinti che devo considerare. Tieni presente che TUTTI i blocchi ereditano da un singolo blocco astratto, queste sono classificazioni del comportamento delle classi concrete, da non confondere con gli abstract aggiuntivi:

  • Generico: a Generics può essere aggiunto qualsiasi altro tipo di blocco

  • Finale - Impossibile aggiungere blocchi figlio.

  • Padre - Un genitore è un blocco a cui può essere aggiunto solo un tipo specifico ma può essere aggiunto a qualsiasi generico

  • Bambino - Può essere aggiunto solo a un genitore specifico. Questo blocco può anche condividere le proprietà di un Generic, Parent o Final

Per riassumere l'obiettivo: dato un nome di classe di blocco concreto, generare un elenco di tutte le classi di blocchi concreti che possono essere aggiunte ad esso.

Soluzioni proposte finora :

Aggiungi una proprietà chiamata "blockClassification" al blocco astratto che la definisce come generica, finale, padre o figlio e codifica la logica in una funzione di convalida che viene chiamata nel da addBlock (blocco $) metodo.

Questa soluzione non mi piace per una serie di motivi, ma soprattutto non mi dà ancora un chiaro percorso per definire cosa possono essere concessi genitori e figli concreti. Ad esempio, ho una classe concreta \ Block \ Tabs che è un genitore. Può solo aggiungere blocchi \ Block \ Tabs \ Panel e \ Block \ Tabs \ Panel può essere aggiunto solo a \ Block \ Tabs . Ulteriori proprietà dovrebbero essere aggiunte per costruire quelle relazioni e queste proprietà potrebbero essere esaminate per ricavare la "classificazione dei blocchi" che rende questo approccio poco pratico.

Aggiungi la proprietà allowedChildTypes al blocco che definisce un elenco di classi concrete che possono essere aggiunte. Per facilità d'uso, i 2 valori hanno un significato specifico, un * che rappresenta tutti i tipi di blocco e il valore NULL che significa che non è possibile aggiungere alcun blocco. Per tutti gli altri casi viene fornita una stringa delimitata da nomi di classi e sono consentite solo le classi o le classi che ne ereditano

Sono propenso a questa direzione perché PHP mi fornisce l'operatore InstaceOf. Posso usarlo per fare i controlli, quando ho nuove classi concrete che estendono il set base, quel controllo restituirà gli stessi risultati della sua classe genitore. La mia unica esitazione è che mentre questo metodo risolve definitivamente il problema di "Dato un nome di classe di blocco concreto, genera un elenco di", sento che mi limiterà in futuro in quanto posso solo eseguire la ricerca lungo l'albero e non tornare indietro su. Cioè "Dato un nome classe di blocco figlio, genera un elenco di blocchi padre che può essere aggiunto a"

Sono sicuro di non essere il solo ad aver mai incontrato il problema, quindi la mia domanda alle masse è: qual è il modo migliore per affrontare / risolvere questo problema?

Informazioni aggiuntive:

Se ti stai chiedendo come ottenere una lista di tutte le classi in primo luogo, il framework implementa PSR-0 in modo da analizzare l'albero delle directory per ricavare tutti i nomi di classe, quindi istanzializzarli dinamicamente.

$allClassNames = scanDirectories(); // Returns array of strings ex: array('\Block\Classname1','\Block\Classname2'); 
foreach($allClassNames as $className){
  $object = new $className();
}
    
posta Steve Zurek 16.05.2014 - 22:39
fonte

2 risposte

1

Non conosco il PHP, ma la mia comprensione dagli altri commenti è che non è strongmente tipizzato. Se stavo cercando di risolvere questo problema in C # o Java, la mia preferenza sarebbe quella di usare l'ereditarietà sul modello di composizione. "È un" invece di "ha un".

Quello che vorrei fare è avere una classe base per ciascuna delle tue classi di blocchi; generico, definitivo, genitore, figlio. Vorrei inoltre creare una classe helper per verificare i tipi di classi concrete che possono essere aggiunte a ciascuna classe di blocchi. Ad esempio

GenericBlock di classe pubblica implementa BlockAbstract   {       _helper = new HelperClass ();

  public function AddBlock(block)
  {
      _helper.CheckBlockType(block);
  }

}

Nella classe helper, vorrei avere metodi di supporto per ciascuno dei tipi di blocchi necessari. Il vantaggio è che se è necessario modificare o aggiungere più regole, è facilmente estensibile attraverso una classe helper. Se avessi a che fare con un linguaggio tipizzato, le mie regole per le classi che potrebbero essere aggiunte alla classe di blocco sarebbero basate sul tipo. Sembra che le tue regole siano più aperte, quindi potresti voler semplicemente avere liste di nomi di classi nei tuoi metodi di supporto. Non elegante, ma farà il suo lavoro.

    
risposta data 17.05.2014 - 05:12
fonte
1

Il composito spesso va di pari passo con il modello Builder per risolvere esattamente il problema che hai menzionato qui. Puoi creare una classe separata, chiamarla PageBuilder (o quello che ti piace) e spostare tutta la logica di costruzione in quella classe. Sebbene io non conosca le parti interne del tuo codice ma considerando una coppia di generatori / generatori molto generica, ecco un codice di esempio per il tuo ref che può aiutarti a iniziare.

class PageEelement()
{
    //properties & methods of basic composite element 
    public add(PageEelementInterface $element){

    }
    public remove(PageEelementInterface $element){

    }

    ....
    ....
}

/* Next comes the Builder */

class PageBuilder(){

    $protected $page;

    public construct(PageEelementInterface $page){
        $this->page = $page;
    }         
    public addBlock($params){
        //block creation with rules
        if($param == x)
            $block =  new PageElement($paramas);
        } else {
            $block =  new PageElement($paramas);
        }
        $this->page->add($block);

    }

    public function addPanel($params){
        //panel building conditions and validation 
    }

    public render(){
        $this->page->render();
    }
}

e puoi chiamarlo come

$page = new PageElement('title', 'width', 'height');
$pageBuilder = new PageBuilder($page);
$pageBuilder->addBlock('title', 'width', 'height');
$pageBuilder->addPanel(params);

$pageBuilder->render();
    
risposta data 27.07.2015 - 05:23
fonte

Leggi altre domande sui tag