2015-11-12 7 views
5

Ho una classe chiamata Assembly che nasconde l'implementazione dei prodotti sottostanti. ProductA può avere un set di getter, ProductB può avere un altro.Come si può far conoscere il codice PHP di sottotipi di classe specifici passati ad esso, quando si utilizza il polimorfismo?

Mentre PHP è abbastanza tollerante e se non si mescolano prodotti e getter, il mio codice funziona, ma non v'è alcuna protezione offerta per quando faccio rovinare e popolare Assembly con ProductA, ma poi utilizzare un getter da ProductB.

Inoltre, il mio IDE conosce i metodi di entrambe le classi, quindi non è disponibile il completamento automatico quando lavoro all'interno di Assembly con getter da Product.

voglio di più il codice a prova di proiettile, quello in cui Assemblysa o è a conoscenza di che Product sottotipo attualmente ospita. c'è un modo per farlo?

esempio riportato di seguito

class Assembly 
{ 
    private $product; 

    public function setProduct(Product $product) { 
     $this->product = $product; 
    } 

    public function getA() { 
     return $this->product->getA(); 
    } 

    public function getB() { 
     return $this->product->getB(); 
    } 
} 

class Product { } 

class ProductA extends Product 
{ 
    public function getA() { 
     return "A"; 
    } 
} 

class ProductB extends Product 
{ 
    public function getB() { 
     return "B"; 
    } 
} 

//set assembly 
$assembly = new Assembly(); 

//define product sub-type 
$product = new ProductB(); 

//place product into assembly 
$assembly->setProduct($product); 

//assembly has no clue which subtype it has 
print $assembly->getB(); // "B" 
print $assembly->getA(); // prints nothing 

Interface Suggerimento

interface CompositeProductInterface 
{ 
    public function getA(); 
    public function getB(); 
} 

//update code in Assembly to: 
public function setProduct(CompositeProductInterface $product) 
{ 
    $this->product = $product; 
} 

Questo mi dà completamento automatico nella mia IDE, che è bello, ma non risolve le altre questioni. Inoltre sono un po 'inquieto nel mettere insieme le cose in un unico prodotto "composito" .... Sembra più un work-around. Nel mio caso specifico, ho due prodotti molto simili che differiscono su alcune cose minute come alcune proprietà.

Real World Esempio

Entrambi i modelli del prodotto A e B hanno un disegno tecnico in cui sono elencate le variabili per identificare diverse dimensioni del prodotto. Le dimensioni nominate sono simili su entrambi i prodotti. Ad esempio, M1 sul prodotto A indica la stessa dimensione fisica sul prodotto B. Tuttavia, il prodotto A presenta caratteristiche non presenti sul prodotto B e viceversa.

Se si prova ancora troppo ipotetica, le dimensioni reali sul disegno effettivo sono elencati come B2 e B3. Queste dimensioni (B2, B3) non sono presenti sull'altro modello di prodotto. Voglio poter usare un costrutto assembly per elencare le variabili sul disegno, tra cui M1, B2, B3 e così via. Potrei faccio con

print $assembly->getM1(); //works for both products 
print $assembly->getB2(); //works for one product only 
print $assembly->getB3(); //same as above 

Goal

obiettivo è di avere una sola classe (montaggio), responsabile della messa in vendita variabili di quota con nome, e ogni prodotto conosco solo per sé. Così

  • Assemblea dice: "So tutto sulle variabili denominate voglio elencare (M1, B2, B3), a prescindere dal modello di prodotto"
  • prodotto A dice: "Io so solo quello che contengono. Contengo denominata dimensione A. Non so cosa sia B né mi interessa
  • Il prodotto B dice "Conosco solo B" ...

risposta

1

Non ritengo che questa sia la soluzione giusta, ma quello che chiedi potrebbe essere raggiunto nel modo seguente.

get_class($product); 

Indica quale classe è $product. Che porterebbe al seguente codice:

private $product; 
private $productClass; 

public function setProduct(Product $product) { 
    $this->product = $product; 
    $this->productClass = get_class($product); 
} 

Oppure:

public function getProductClass(){ 
    return get_class($this->$product); 
} 

che potrebbe porta ad un controllo simile a:

public function getA() { 
    if($this->productClass === "ProductA") 
     return $this->product->getA(); 
} 

Come ben documentato in:

http://php.net/manual/en/function.get-class.php

+0

cool. puoi anche usare il costrutto 'ProductA :: class' invece di' "ProductA" 'con il vantaggio del tipo hinting dall'interprete IDE/PHP – Dennis

+0

È una soluzione migliore per la comparazione. –

0

So che esiste già una risposta accettata ma vorrei dare un approccio OOP più corretto.

La soluzione effettiva funzionerà solo perché PHP utilizza il tipo reale e non limita l'ambito dell'oggetto dal tipo di firma del metodo.

Per evitare divergenze di complessità, non consiglio un codice in cui è necessario aggiungere del codice in una classe (il proprio assieme) ogni volta che ne aggiungi un altro (un nuovo tipo di prodotto).


Nel tuo caso, vorrei utilizzare uno pattern adapter.

Ecco le schede:

interface AssemblyInterface 
{ 
    function getA(); 
    function getB(); 
} 

class AssemblyA implements AssemblyInterface 
{ 
    private $product; 

    public function setProduct(ProductA $product) { 
     $this->product = $product; 
    } 

    public function getA() { 
     return $this->product->getA(); 
    } 

    public function getB() { 
     return; 
    } 
} 

class AssemblyB implements AssemblyInterface 
{ 
    private $product; 

    public function setProduct(ProductB $product) { 
     $this->product = $product; 
    } 

    public function getA() { 
     return; 
    } 

    public function getB() { 
     return $this->product->getB(); 
    } 
} 

Qui i vostri adaptees:

class ProductA 
{ 
    public function getA() { 
     return "A"; 
    } 
} 

class ProductB 
{ 
    public function getB() { 
     return "B"; 
    } 
} 

Ora, è possibile utilizzare un pattern factory per recuperare gli assembly.

Qui i vostri creatori:

interface AssemblyCreatorInterface 
{ 
    function isSupportingProduct($product); 
    function createAssembly($product); 
} 

class AssemblyCreatorA implements AssemblyCreatorInterface 
{ 
    public function isSupportingProduct($product) { 
     return $product instanceof ProductA; 
    } 

    public function createAssembly($product) { 
     return new AssemblyA($product); 
    } 
} 

class AssemblyCreatorB implements AssemblyCreatorInterface 
{ 
    public function isSupportingProduct($product) { 
     return $product instanceof ProductB; 
    } 

    public function createAssembly($product) { 
     return new AssemblyB($product); 
    } 
} 

Qui di montaggio della fabbrica:

class AssemblyFactory 
{ 
    private $assemblyCreators = array(); 

    public function addCreator(AssemblyCreatorInterface $assemblyCreator) { 
     $this->assemblyCreators = $assemblyCreator; 
    } 

    public function get($product) { 
     // Find the good creator for your product. 
     foreach ($this->assemblyCreators as $assemblyCreator) { 
      if ($assemblyCreator->isSupportingProduct($product)) { 
       // Create and return the assembly for your product. 
       return $assemblyCreator->createAssembly($product); 
      } 
     } 

     throw new \InvalidArgumentException(sprintf(
      'No available assembly creator for product "".', 
      get_class($product); 
     )); 
    } 
} 

Infine, ecco un esempio di programma:

// Process dependency injection. 
// (you should be able to do it only once in your code) 
$assemblyCreatorA = new AssemblyCreatorA(); 
$assemblyCreatorB = new AssemblyCreatorB(); 

$assemblyFactory = new AssemblyFactory(); 
$assemblyFactory->addCreator($assemblyCreatorA); 
$assemblyFactory->addCreator($assemblyCreatorB); 

// Retrieve assemblies from products. 
$productA = new ProductA(); 
$productB = new ProductB(); 

$assemblyA = $assemblyFactory->get($productA); 
$assemblyB = $assemblyFactory->get($productB); 

$assemblyA->getA(); // 'A' 
$assemblyA->getB(); // null 
$assemblyB->getA(); // null 
$assemblyB->getB(); // 'B' 

Questo è un po 'di più codice ma rispetta le firme dei metodi e gestirà la crescente complessità della tua applicazione. Con questa soluzione, se si dispone di 100 diversi tipi di prodotto, ognuno sarà indipendente dagli altri (una classe Assembly, una classe Product e una classe Creator). Immagina la tua classe di assemblaggio se devi codificare la specificità di ciascun prodotto per ciascun getter in esso contenuto!

Problemi correlati