Classe figlio che accede al metodo dei suoi genitori dal metodo Ancestor

3

Mi trovo in questo momento a sbattere la testa con il seguente problema (in PHP):

Ho una classe base astratta, che ha un metodo non astratto, ereditata e invariata su tutta la catena ereditaria (che è a 3 livelli in questo momento, solo il primo livello è astratto).

Questo metodo non astratto contiene la logica per calcolare un percorso del disco, che viene eseguito prendendo in considerazione il nome della classe. Sia i bambini che i nipoti adottano questo metodo / algoritmo senza modifiche.

Tuttavia, desidero implementare la funzionalità in modo che se un Nipote non ha il proprio percorso specifico sul disco, tenterà di utilizzare il percorso del suo genitore. Solo il genitore può calcolare questo percorso (solo il genitore conosce il proprio nome) - Non voglio usare il reflection per memorizzare il nome del genitore in una variabile, e mi piacerebbe anche che funzioni in modo omogeneo - se la catena di ereditarietà è più lunga , dovrebbe cercare di risalire la catena ereditaria per chiedere a qualcuno dei suoi antenati un "luogo" (il percorso sopra indicato).

Una chiamata di parent::Method() in PHP (come in altri linguaggi OOP) proverà a chiamare il genitore della classe corrente , non il genitore della classe dell'oggetto in fase di esecuzione.

In PHP, si può farlo attraverso Reflection, ma sono più o meno persuaso che ciò infrange alcuni principi dell'OOP di cui non sono a conoscenza.

//Abstract class BaseSomething...
if(!$this->Calculation()) {
  $parentClass = (new \ReflectionObject($this))->getParentClass();
  if(!$parentClass->isAbstract()) {
    $method = $parentClass->getMethod('Calculation');
    $method->setAccessible(true);
    $this->result= $method->invoke($this, $arg ...);
  }
}

Funziona, fa il lavoro. Permette una pseudo-ricorsione di sorta, fermandosi al primo antenato che può fornirci ciò di cui abbiamo bisogno, ma qualcosa deve essere più o meno sbagliato lungo la strada.

Una possibile soluzione per cui penso stia avendo una catena di costruttori, in cui ogni classe aggiunge il suo nome a una matrice. Ciò implicherebbe forzare ogni bambino a chiamare il costruttore del genitore e anche ad aggiungere il suo nome alla matrice, che non è neanche carina.

Forse il problema risiede in ciò che sto cercando di fare - usare l'ereditarietà in un modo più comprensivo di quello che è stato inteso fare.

Mi piacerebbe sentire alcune riflessioni su come questo possa essere rifattorizzato in modo tale da non infrangere le regole, o forse qualcuno potrebbe prendere in considerazione il mio caso d'uso, usando questo codice di ridefinizione delle basi è "ammissibile".

Modifica: mi scuso per aver lasciato che questa domanda si allontanasse per più di un mese e sottrassi la mia responsabilità di aggiungere esempi e rispondere alle domande. Era, in parte, solo una pigrizia di base.

Proseguendo nella stessa vena del post originale, questo è il modo in cui i tre livelli sembrano approssimativamente (semplificati il più possibile, quasi identici a quelli precedenti):

abstract class BaseWidget extends \Illuminate\Routing\Controller {
    private function getPath($file) {
         $name = return get_called_class(); //Uses get_class_name
         $file = $some_base_path . '/' . $name . '/';
         if(file_exists($file)) {
             return $file;
         } else {
             $refl = new \ReflectionObject($this);
             $parentRefl = $refl->getParentClass();
             $methodRefl = $parentRefl->getMethod('getPath');
             $methodRefl->setAccessible(true);
             $path = $methodRefl->invoke($this);
             return $path;
         }
    }   
}

class Widget extends BaseWidget {
     //This guy resides in App/Widgets/Widget/Widget.php,
     //and has a view in Widget/Views/View.blade.php
     //When $widget->getPath('Views/View.blade.php') is called,
     //it will know how to 'find itself' and where to look for its own
     //view         
}

class SpecialisedWidget extends Widget {
     //This guy resides in ...Widgets\SpecialisedWidget.php
     //Its 'views' folder is empty, so when it has to find it,
     //it will instead borrow it from its parent. If this class
     //also had a child with no view, it would go up to Widget on the
     //inheritance chain.
}

Riconosco che sto usando l'ereditarietà per fare qualcosa che l'ereditarietà non dovrebbe fare. Tuttavia, la funzionalità che ho dovuto implementare - Un Widget che prende in prestito le viste dal suo genitore, che sono relative alla posizione del genitore sul disco - di cui il bambino non è a conoscenza - ha reso necessario cheat un po '.

    
posta TotoTitus 04.02.2016 - 21:45
fonte

3 risposte

1

Senza conoscere troppi dettagli sulle responsabilità di questa gerarchia di classi, sembra che potrebbe violare SRP.

Considererei l'utilizzo dell'iniezione del costruttore per passare un oggetto in grado di calcolare un percorso. Questa è un'applicazione del modello di strategia .

In questo modo si disaccoppiano le responsabilità della classe dalla responsabilità di sapere con quale percorso lavorare: l'oggetto sa solo che può chiamare un metodo su qualche altro oggetto e ottenere un percorso di directory da usare.

    
risposta data 09.02.2016 - 06:46
fonte
1

perché non itera attraverso i livelli? che dovrebbe risolvere il tuo problema senza sovraccarico:

protected function getPath()
{
  $classname = get_class($this);
  do
  {
    $path = BASEPATH. '/'. $classname .'/';
    if (file_exists($path)) { return $path; }
    $classname = get_parent_class($classname);
  } while ($classname);
  return NULL;
}

o, nel tuo codice, prova { parent::getPath(); } nel ramo else. Non ho ancora utilizzato get_called_class , ma dalla descrizione, anche questo dovrebbe fare il trucco.

    
risposta data 19.06.2016 - 22:36
fonte
0

Non parlo PHP ma mi sembra che questo possa essere risolto con un metodo che restituisce il percorso. Definirlo nell'antenato, solo i discendenti che hanno un percorso su disco lo implementano. Passerai automaticamente in qualsiasi punto della catena di cui hai bisogno per trovare qualcuno che lo implementa.

Se il genitore non fornisce ciò che vuoi, ma qualcuno più in alto nella struttura ti servirà un po 'più complesso - l'implementazione cercherà il file, se fallisce lo passa al genitore.

Nota che in entrambi i casi penso che la logica appartenga all'antenato, le implementazioni chiameranno semplicemente la logica genitore con la loro posizione corrente.

    
risposta data 18.10.2016 - 04:29
fonte

Leggi altre domande sui tag