2017-06-17 45 views
1

考慮下面的例子:Php,可以使用DI特性嗎?

class MyClass 
{ 
    public function doSomething() 
    { 
     $this->injected->getIt(); 
    } 
} 

到目前爲止如此簡單(除了injected沒有注入)。所以,在完整版:

class MyClass 
{ 
    /** 
    * @var Injected 
    */ 
    private $injected; 

    public function __constructor(Injected $injected) 
    { 
     $this->injected = $injected; 
    } 

    public function doSomething() 
    { 
     $this->injected->getIt(); 
    } 
} 

但我覺得它是enoromous。爲什麼要用大量的DI代碼污染我的班級?讓我們分成兩個實體:

trait MyClassTrait 
{ 
    /** 
    * @var Injected 
    */ 
    private $injected; 

    public function __constructor(Injected $injected) 
    { 
     $this->injected = $injected; 
    } 
} 

class MyClass 
{ 
    use MyClassTrait; 

    public function doSomething() 
    { 
     $this->injected->getIt(); 
    } 
} 

它更好,雖然我從來沒有見過任何人使用它像這樣。這是一個好方法嗎?

+2

在一週之內,你將打開'MyClass',然後你會問自己:「到底是什麼'injected'?」。有這樣的理由嗎?你只需隱藏你的依賴關係。你會有100個班級和100個特質,出於這個原因?這不好 – Federkun

+1

imo工廠+反射是更好的方式 – Kazz

+0

@Kazz究竟如何? –

回答

1

例如像這樣:

<?php 

class Factory 
{ 
    private $services = []; 

    public function __construct() { 
     $this->services[self::class] = $this; 
    } 

    public function getByType($type){ 
     if(isset($services[$type])){ 
      return $services[$type]; 
     } 

     if(class_exists($type)){ 
      $reflection = new ReflectionClass($type); 
      $constructor = $reflection->getConstructor(); 

      $parameters = []; 
      if($constructor) 
      foreach($constructor->getParameters() as $parameter){ 
       if($parameter->getClass()) { 
        $parameters[] = $this->getByType($parameter->getClass()->name); 
       } else if($parameter->isDefaultValueAvailable()){ 
        $parameters[] = $parameter->getDefaultValue(); 
       } 
      } 

      return $services[$type] = $reflection->newInstanceArgs($parameters); 
     } // else throw Exception... 
    } 
} 

abstract class DI 
{   
    public function __construct(Factory $factory) {   
     $reflection = new ReflectionClass(get_class($this)); 
     foreach($reflection->getProperties() as $property){ 
      preg_match('/@var ([^ ]+) @inject/', $property->getDocComment(), $annotation); 
      if($annotation){ 
       $className = $annotation[1]; 
       if(class_exists($className)){ 
        $property->setAccessible(true); 
        $property->setValue($this, $factory->getByType($className)); 
       } // else throw Exception... 
      } 
     } 
    } 
} 

class Injected 
{ 
    public function getIt($string){ 
     echo $string.'<br />'; 
    } 
} 


class DIByConstructor 
{ 
    /** @var Injected */ 
    private $byConstructor; 

    public function __construct(Injected $injected) { 
     $this->byConstructor = $injected; 
    } 

    public function doSomething() 
    { 
     echo 'Class: '.self::class.'<br />'; 
     $this->byConstructor->getIt('By Constructor'); 
     echo '<br />'; 
    }  
} 

class DIByAnnotation extends DI 
{ 
    /** @var Injected @inject */ 
    private $byAnnotation; 

    public function doSomething() 
    { 
     echo 'Class: '.self::class.'<br />'; 
     $this->byAnnotation->getIt('By Annotation'); 
     echo '<br />'; 
    }  
} 

class DIBothMethods extends DI 
{ 
    /** @var Injected */ 
    private $byConstructor; 

    /** @var Injected @inject */ 
    private $byAnnotation; 

    public function __construct(Factory $factory, Injected $injected) { 
     parent::__construct($factory); 
     $this->byConstructor = $injected; 
    } 

    public function doSomething() 
    { 
     echo 'Class: '.self::class.'<br />'; 
     $this->byConstructor->getIt('By Constructor'); 
     $this->byAnnotation->getIt('By Annotation'); 
     echo '<br />'; 
    }  
} 

$factory = new Factory(); 

$DIByConstructor = $factory->getByType('DIByConstructor'); 
$DIByConstructor->doSomething(); 

$DIByAnnotation = $factory->getByType('DIByAnnotation'); 
$DIByAnnotation->doSomething(); 

$DIBothMethods = $factory->getByType('DIBothMethods'); 
$DIBothMethods->doSomething();