2015-11-12 3 views
5

Ich habe eine Klasse namens Assembly, die die Implementierung der zugrunde liegenden Produkte versteckt. kann einen Satz von Gettern haben, ProductB kann einen anderen haben.Wie kann PHP-Code auf bestimmte Klassen-Subtypen aufmerksam gemacht werden, wenn er Polymorphie verwendet?

Während PHP recht nachsichtig ist, und wenn ich nicht gleichzeitig auf Produkte und Getter, wird mein Code arbeiten, aber es gibt keinen Schutz bot, wenn ich mess up tun und füllen Assembly mit ProductA, aber dann einen Getter verwenden aus ProductB.

Auch meine IDE kennt die Methoden aus beiden Klassen, also gibt es keine automatische Vervollständigung, wenn ich innerhalb Assembly mit Getters von Product arbeite.

ich mehr kugelsicheren Code wollen, ein, wo Assemblyweiß oder von die Product Subtyp bewusst ist es zur Zeit beherbergt. Gibt es einen Weg dazu?

Probe unter

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 

Schnittstelle Anregung

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

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

Das gibt mir die automatische Vervollständigung in meinem IDE, das ist schön, aber nicht meine andere Probleme zu lösen. Außerdem bin ich ein wenig unheimlich, wenn ich Dinge zu einem einzigen "zusammengesetzten" Produkt zusammenbilde ... Es scheint eher wie ein Workaround zu sein. In meinem speziellen Fall habe ich zwei sehr ähnliche Produkte, die sich in einigen winzigen Dingen wie einigen Eigenschaften unterscheiden.

Real World Beispiel

Beide Produkt Modelle A und B haben eine technische Zeichnung, die Variablen listet verschiedene Produktdimensionen zu identifizieren. Benannte Dimensionen sind in beiden Produkten ähnlich. Zum Beispiel bedeutet M1 auf Produkt A die gleiche physische Dimension auf Produkt B. Produkt A hat jedoch Merkmale, die auf Produkt B nicht vorhanden sind und umgekehrt.

Wenn das immer noch zu hypothetisch fühlt, sind die tatsächlichen Abmessungen auf der tatsächlichen Zeichnung aufgeführt als B2 und B3. Diese Dimensionen (B2, B3) sind auf dem anderen Produktmodell nicht vorhanden. Ich möchte in der Lage sein, ein Assembly-Konstrukt zu verwenden, um Variablen in der Zeichnung aufzulisten, einschließlich M1, B2, B3 und so weiter. Ich konnte dies mit

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

Tor

Tor eine einzige Klasse zu haben ist (Montage) verantwortlich für benannte Bemaßungsvariablen Auflistung, und jedes Produkt kennen sich nur. So

  • Versammlung sagt: "Ich weiß alles über die genannten Variablen I aufgelistet werden sollen (M1, B2, B3), unabhängig von Produktmodell"
  • Produkt A sagt: „Ich weiß nur, was ich enthalten. I enthalten Benanntes Maß A. Ich weiß nicht, was B ist und interessiere mich auch nicht für
  • Produkt B sagt "ich kenne nur B" ...

Antwort

1

Ich glaube nicht, dass dies die richtige Lösung ist, aber was Sie verlangen, könnte auf folgende Weise erreicht werden.

Erzähle Ihnen, welche Klasse $product ist. Welche den folgenden Code führen würde:

private $product; 
private $productClass; 

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

Oder:

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

was dazu führte, ähnlich wie eine Überprüfung könnte:

:

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

in As gut dokumentiert

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

+0

kühlen. Sie können auch 'ProductA :: class' Konstrukt anstelle von' "ProductA" 'mit Vorteil von Typ Hinting von IDE/PHP-Interpreter – Dennis

+0

ist eine bessere Lösung für die Komparation. –

0

Ich weiß, dass es bereits eine akzeptierte Antwort gibt, aber ich möchte einen korrekteren OOP-Ansatz geben.

Die tatsächliche Lösung funktioniert nur, weil PHP den realen Typ verwendet und den Umfang des Objekts nicht vom Methodensignaturtyp einschränkt.

Um Komplikationsdivergenz zu vermeiden, würde ich keinen Code empfehlen, bei dem Sie jedes Mal, wenn Sie einen neuen Produkttyp hinzufügen, Code in einer Klasse (Ihrer Assembly) hinzufügen müssen.


In Ihrem Fall würde ich eine pattern adapter verwenden.

hier Ihre Adapter:

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(); 
    } 
} 

Ihre adaptees hier:

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

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

Jetzt können Sie eine pattern factory verwenden Ihre Baugruppen abzurufen.

hier Ihre Schöpfer:

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); 
    } 
} 

Hier können Sie Montagewerk:

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); 
     )); 
    } 
} 

Schließlich ist hier ein Programm Beispiel:

// 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' 

Das ist ein bisschen mehr Code, aber dies respektiert Ihre Methode Signaturen und wird die wachsende Komplexität Ihrer Anwendung bewältigen. Wenn Sie bei dieser Lösung 100 verschiedene Produkttypen haben, ist jeder unabhängig von anderen (eine Klasse Assembly, eine Klasse Product und eine Klasse Creator). Stellen Sie sich Ihre Assembly-Klasse vor, wenn Sie die Spezifität jedes Produkts für jeden darin enthaltenen Getter codieren müssen!

Verwandte Themen