Hai due problemi qui:
-
È necessario serializzare un modello di oggetto con differenze nei nomi delle chiavi, ma contenente gli stessi dati di base (un prezzo e un'opzione)
-
Devi eseguire alcuni calcoli specifici in modo generico
Questo richiede due soluzioni.
Per prima cosa, affrontiamo i calcoli di esecuzione, perché questa soluzione ci consentirà di risolvere il nostro problema di serializzazione dei dati. Quello che vuoi veramente è il Pattern di strategia :
In computer programming, the strategy pattern (also known as the policy pattern) is a software design pattern that enables an algorithm's behavior to be selected at runtime. The strategy pattern
- defines a family of algorithms,
- encapsulates each algorithm, and
- makes the algorithms interchangeable within that family.
Questo è esattamente ciò che stai cercando di fare. Innanzitutto, vuoi definire un'interfaccia pubblica per i tuoi oggetti strategia che:
- Calcola il prezzo
- Restituisce una sorta di "nome" per la strategia (utile per creare l'array JSON)
- Restituisce il prezzo "opzione" (ancora utile per la creazione dell'array JSON)
interface PricingStrategy
{
public function calculatePrice();
public function getName();
public function getOption();
}
Poiché l'algoritmo per la strategia di determinazione dei prezzi è lo stesso nel tuo esempio di codice e cambiano solo alcuni valori, possiamo implementare una classe base astratta e quindi estenderla per fornire valori specifici:
abstract class ProductPricingStrategy implements PricingStrategy
{
private $name;
private $basePrice;
private $cost;
private $option;
protected function __construct($name, $basePrice, $cost, $option) {
$this->name = $name;
$this->basePrice = $basePrice;
$this->cost = $cost;
$this->option = $option;
}
protected function getBasePrice() {
return $this->basePrice;
}
protected function getCost() {
return $this->cost;
}
public function getOption() {
return $this->option;
}
public function calculatePrice() {
return $this->basePrice + $this->cost + $this->option;
}
public function getName() {
return $this->name;
}
}
Questo ti dà la struttura di base per creare facilmente strategie di prezzo specifiche:
class PumpPricingStrategy extends ProductPricingStrategy
{
public function __construct() {
parent::__construct('pump', 1000, 250, 49.99);
}
}
class MotorPricingStrategy extends ProductPricingStrategy
{
public function __construct() {
parent::__construct('motor', 5000, 1500, 350);
}
}
class PartPricingStrategy extends ProductPricingStrategy
{
public function __construct() {
parent::__construct('part', 50, 20, 4.99);
}
}
Ogni classe concreta chiama il costruttore genitore protected
e fornisce tutti i valori necessari per la strategia. Ora, abbiamo solo bisogno di un oggetto "fabbrica" per fornire un accesso semplice e parametrato a queste strategie:
class PricingStrategyFactory
{
private $strategies;
public function __construct() {
$this->strategies = array(
new PumpPricingStrategy(),
new MotorPricingStrategy(),
new PartPricingStrategy()
);
}
public function find($name) {
foreach ($this->strategies as $strategy) {
if ($strategy->getName() == $name) {
return $strategy;
}
}
throw new Exception("Pricing Strategy '$name' not found");
}
}
Infine, possiamo rifattorizzare il codice in questione e ridurlo a solo 6 righe di codice che non richiedono alcun if
s, switch
s, and
s (o buts):
$factory = new PricingStrategyFactory();
$pricing = $factory->find('motor');
$json = array(
'total' => $pricing->calculatePrice(),
"{$pricing->getName()}_option_1" => $pricing->getOption()
);
Il metodo getName
viene utilizzato per generare la chiave di un array JSON univoco che la tua risposta è in attesa. Il metodo calculatePrice
diventa una scatola nera che incapsula l'algoritmo, quindi il resto della tua base di codice non ha bisogno di saperlo.
Noterai inoltre che l'interfaccia PricingStrategy
non fa riferimento a nessun altro posto se non alla definizione dell'interfaccia e alla classe ProductPricingStrategy
. Nell'esempio del codice breve sopra non vedrai il vantaggio dell'interfaccia. Quando devi trasferire questo oggetto della strategia di prezzo, ne vedrai i vantaggi.
In primo luogo, facciamo finta di avere un framework MVC fittizio e abbiamo creato una classe "controller" chiamata "ProductPricesController". Il framework MVC indirizza le richieste GET per /product_prices/calculate?type=ABC
al metodo ProductPricesController#calculate
:
class ProductPricesController extends Controller
{
// GET: /product_prices/calculate?type=motor
public function calculate() {
$type = $_GET['type'];
$factory = new PricingStrategyFactory();
$pricing = $factory->find($type);
$json = $this->getPriceJson($pricing);
echo json_encode($json);
}
private function getPriceJson(PricingStrategy $pricing) {
return array(
'total' => $pricing->calculatePrice(),
"{$pricing->getName()}_option_1" => $pricing->getOption()
);
}
}
Il metodo getPriceJson
ha un suggerimento tipo PHP: PricingStrategy
, che è il nome della nostra interfaccia. Questo aiuta lo sviluppatore perché fornisce informazioni su ciò che questo metodo si aspetta e aiuta l'applicazione perché l'interfaccia diventa un "contratto" tra il controller e la strategia di pricing in modo che i due possano interagire in modo prevedibile e modulare.