Ho una classe che funge da libreria di funzioni per vari prodotti. Per calcolare i suoi dati, la funzione deve attualmente essere a conoscenza di tutti i nomi dei prodotti. A seconda del prodotto che chiama la funzione, restituisce le risposte specifiche del prodotto.
Il codebase è abbastanza contorto da dove le classi sono legate l'una all'altra in modi rotondi e sono disordinate e non hanno una struttura chiara immediatamente riconoscibile. Quindi sto rifattorizzando spesso senza uno scopo specifico in mente, ma in questo caso voglio refactoring per
- ridurre la necessità della biblioteca di fare affidamento sulla conoscenza del prodotto
- forse posso pulire il codice in modo sufficiente a dove posso usare il polimorfismo - creare un'istanza di un prodotto specifico e poi avere un codice di blocco dall'aspetto generico chiamare le funzioni specifiche del prodotto
Se io ho bisogno di per fare questo tipo di refactoring, non lo so, poiché dopotutto "Le cose funzionano bene ora", tranne che sono difficili da seguire e gli errori sono difficili da rintracciare. Tuttavia, il mio obiettivo e la mia domanda è di "rimuovere la dipendenza da conoscenze specifiche del prodotto dalla funzione calcVar
".
Domanda : come rimuovo la consapevolezza del prodotto dalla funzione calcVar()
di seguito.
Esempio di lavoro di seguito :
class Product{}
class Graph{}
class ProductA extends Product
{
public $calc;
public function __construct() {
$this->calc = new CalcLibrary();
$this->calc->spec->product = "A";
}
}
class GraphB extends Graph
{
public $calc;
public function __construct() {
$this->calc = new CalcLibrary();
$this->calc->spec->product = "B";
}
}
class Specs
{
public $var;
public $product;
//loads different specs based on product name
function __construct() {
if ($this->product == "A") $this->var = 3;
if ($this->product == "B") $this->var = 4;
$this->var = 5;
}
}
class CalcLibrary
{
public $spec;
public function __construct() {
$this->spec = new Specs();
}
//problem - library holding formulas for "Var" is product-aware
//loads different formulas based on product name
public function calcVar() {
if ($this->spec->product == "A")
{
//somewhat different formulas for different products
if ($this->spec->var <= 5) return 3 * $this->spec->var;
else return 0;
}
if ($this->spec->product == "B")
{
if ($this->spec->var == 0) return 16;
else return 2 * $this->spec->var;
}
return $this->spec->var;
}
public function output() {
echo $this->calcVar() . "\n";
}
}
// tests should output 15, 10, and 5
(new ProductA())->calc->output();
(new GraphB())->calc->output();
(new CalcLibrary())->output();
I miei pensieri sulla soluzione
Tentativo di soluzione 1
All'inizio la soluzione era facile, pensavo - quando non ero a conoscenza della famiglia di Graph
classi
- sposta calcVar () da CalcLibary in Product class
- elimina le istruzioni if / then / else e lascia la versione generica di calcVar () all'interno della classe Product, ma sposta quelle specifiche in
ProductA/B/C
(classi che estendono il prodotto)
e tutto andava bene fino a quando ho scoperto che esiste un gruppo di classi chiamato GraphA / B / C, che usa anche CalcLibrary.
Tentativo di soluzione 1 di recupero
La soluzione naturale era copiare CalcVar () in Graph
e specializzare fondamentalmente utilizzare lo stesso approccio con Product
. Ma, il problema è che avrò più% funzioni% di%, quelle generiche in Prodotto e in Grafico, e quelle specializzate in ciascuna delle loro sottoclassi. Come in un problema di duplicazione, quando in questo momento non ho la duplicazione di CalcVar()
. Non volevo scambiare un problema con un altro.
Idea della soluzione 2
Un altro modo era di rendere calcVar()
la top class, e avere classi Graph e Product estendere CalcLibrary, avere CalcLibrary contenere calcVar, come faceva prima, ma ora CalcLibrary avrà solo la versione generica di CalcLibrary
, e quindi le singole classi ProductA / B / C e GraphA / B / C hanno versioni calcVar () specifiche del prodotto. Questa sembra una buona soluzione in quanto rimuove la dipendenza dalla conoscenza del prodotto e si occupa della duplicazione. Ma non sono sicuro al 100% che estendere questa libreria sia una buona idea. Non è davvero una biblioteca, se non voglio che sia. È una classe personalizzata, quindi posso renderlo quello che mi serve.
Soluzione 3 Schizzo
Forse tutto ciò di cui ho bisogno è di trovare un nome migliore, quindi invece calcVar
usa CalcLibrary
, e quindi usa la soluzione # 2. Forse più tardi attraverso il refactoring vedrò abbastanza chiaramente nella mia base di codice per vedere un miglior adattamento delle classi (trovare un design migliore), ma quali sono i tuoi pensieri adesso, sul refactoring di questo particolare esempio con le mie esigenze in mente? La "Soluzione 2" è ciò che dovrei fare? Poiché non è così semplice spostare CalcLibrary in alto, con tutte le altre funzioni e vari altri effetti collaterali, forse posso creare una nuova classe con la funzione ClassThatContainsVariousFunctionsUsefulToGraphAndProductClasses
lì, e poi avere Product e Graph estendere quella nuova classe , lascia CalcLibrary da solo e rimuovi le parti inutilizzate di esso mentre muovo le cose in quella nuova classe che ho creato ...
Considerazioni finali
Perché altrimenti, non riesco a vedere un modo per le classi calcVar
e Plot
per usare le funzioni all'interno di Graph
, senza spostare le funzioni all'interno delle rispettive classi specifiche del prodotto in qualche modo. (Voglio dire a parte che CalcLibrary viene utilizzato ora, essendo in una classe separata, ma con il prezzo di essere consapevole del prodotto)
ANCHE, ... il mio terzo schizzo di soluzione con la creazione di CalcLibrary
sembra (forse giusto) ma un po 'esoterico .... come in Non vedo un significato se non quello di far funzionare la situazione esistente con un progetto OO. Voglio dire, un'altra idea è smantellare completamente la classe e fare in modo che ogni classe esegua i propri calc-calc calcoli direttamente all'interno delle classi. Ci saranno molte duplicazioni, ma si sentiranno più "naturali" .....