2014-10-10 11 views
6

W końcu wracam do odkrywania cech w PHP. Pierwsze miejsce, w którym myślałem, że wypróbuję to wtrysk bitów konfiguracyjnych na klasy. Jeśli używam DIC, może mam kod jak to w każdej klasie, która potrzebuje obiekt config:Jak uniknąć zderzeń z cechami PHP używanymi do iniekcji zależności

protected function SetConfig($config) { 
    $this->config = $config; 
} 

protected $config; 

Wydaje się to naturalne dopasowanie do cech, aby uniknąć konieczności ten kod szablonowe wszędzie, więc może tworzyć w ten sposób:

trait Config { 
    protected function SetConfig($config) { 
     $this->config = $config; 
    } 

    protected $config; 
} 

a następnie używać go tak:

class Foo { 
    use Config; 

    public function __construct() { 
     //can now use $this->config 
    } 
} 

to świetnie. Teraz załóżmy, że chcę utworzyć drugą cechę, powiedzmy, do logowania:

trait Logger { 
    protected function SetLogger($logger) { 
     $this->logger = $logger; 
    } 

    protected $logger; 
} 

której mogę użyć takiego:

class Foo { 
    use Logger; 

    public function __construct() { 
     //can now use $this->logger 
    } 
} 

również wspaniałe. Teraz problem pojawia się, jeśli te dwie cechy chcą się nawzajem wykorzystać. Wydaje się całkiem rozsądne, że klasa rejestrator musiałaby mieć obiekt config wstrzykiwany, co oznacza ten sposób:

trait Logger { 
    use Config; 

    protected function SetLogger($logger) { 
     $this->logger = $logger; 
    } 

    protected $logger; 
} 

Ale potem wszystko pęknie, gdy inna klasa używa obu tych cech:

class Foo { 
    use Config, Logger; 

    public function __construct() { 
     //want to use $this->config and $this->logger 
    } 
} 

To oczywiście nie działa, ponieważ bity konfiguracyjne są skutecznie duplikowane w Foo.

Mogę po prostu pominąć element use Config; z cechy Logger, wiedząc, że będzie tam na końcu. Ale wydaje mi się to dziwne, ponieważ tworzy rodzaj zewnętrznej zależności. Co się stanie, jeśli chcę użyć Logger w miejscu, które nie ma jeszcze cechy konfiguracyjnej? To rozwiązanie oznacza również, że muszę cierpieć z powodu mojego IDE (PhpStorm 8) ostrzegającego mnie przed nieznanymi metodami, a nie oferującego autouzupełniania. Zdaję sobie sprawę, że mogę rozwiązać te problemy po kolei, używając metody @method, ale to po prostu umieszczenie szminki na świni, że tak powiem.

Mogę również pseudo konfigurować bitów w Logger, ale to również problematyczne.

Wszystko to ma w sobie trochę zapachu, ale nie doszedłem jeszcze do tego, czy to dlatego, że jest to dla mnie nowy wzór, czy też naprawdę jest śmierdzący wzór. Tak czy siak, nie jestem pewien, jaki jest najlepszy sposób, aby to podejście faktycznie zadziałało.

Wszelkie porady dotyczące najlepszego sposobu rozwiązania tego problemu pod względem cech? A może lepiej unikać cech skracania DIC?

+0

Nigdy nie użyłem "cech" dla DI, ale jeden Sugeruję, że mogę zrobić, że jeśli masz cechy, które wymagają innych cech, możesz skorzystać z zamiany ich na klasy. – Crackertastic

+0

Cecha rejestratora może potrzebować konfiguracji, aby wiedzieć, jakiego poziomu rejestrowania użyć lub gdzie zapisać pliki dziennika. Ale tak na prawdę, pytanie dotyczy czegoś więcej niż tylko tej sprawy - to tylko przykład. Mówiąc szerzej, nie jest rzadkością posiadanie zależności między rdzeniowymi funkcjami. Gdy używasz kodu standardowego (własności i setera) w każdej klasie, nie jest to problemem, ale jest to problem z cechami. W takim przypadku dodanie polecenia 'use Config' do programu Logger nie będzie działać, ponieważ klasa korzystająca zarówno z Logger, jak i Config (powyżej, Foo) wygeneruje błędy krytyczne, ponieważ elementy z Config są zduplikowane. –

+1

Dlatego zadałem to pytanie. Jak już powiedziałem, ma zabawny zapach, ale jednocześnie wydaje się, że cechy są świetnym podejściem do rozwiązania problemu z płytkami charakterystycznymi dla DI. Jakie jest najlepsze podejście do połączenia tych dwóch elementów? –

Odpowiedz

3

Metoda, którą uznałem za użyteczną, to używanie zarówno pobierających, jak i ustawiających. To pozwala ci wymagać, aby określony getter istniał, bez konfliktów z innymi cechami.

trait Config { 
    protected function SetConfig($config) { 
     $this->config = $config; 
    } 

    protected function GetConfig() { 
     return $this->config; 
    } 

    protected $config; 
} 

trait Logger { 
    abstract protected function GetConfig(); 

    protected function SetLogger($logger) { 
     $this->logger = $logger; 
    } 

    protected $logger; 
} 

class Baz { 
    use Config, Logger; 

    // ... 

} 

W Baz, cecha Config dostarcza wymaganej metody abstrakcyjnej, a Baz składa się bez błędów. Jeśli błędnie użyjesz tylko Logger, otrzymasz Błąd krytyczny: Bazę klasy zawiera 1 abstrakcyjną metodę i dlatego należy ją uznać za abstrakcyjną lub zaimplementować pozostałe metody (Baz :: GetConfig)

+0

Wykonuje to zadanie, chociaż nie jest idealne. Głównymi rzeczami, które mnie w tym przeszkadzają są: 1) dokumentacja staje się nieporządna (np. Jeśli ktoś po prostu widzi Logger, skąd wiadomo, skąd ma pochodzić GetConfig())? 2) Należy użyć Config, aby użyć Logger, która jest dziwną zewnętrzną zależnością, którą znowu trudno udokumentować, 3) dotyczy to również dokumentacji, ale w jaki sposób jedna (* może * jedna?) może ją odpowiednio adnotować przy pomocy PHPDoc, aby kodowanie i refowanie kodu IDE działało? Wskazuje na rozwiązanie, które umożliwia wykonanie zadania, ale byłoby wspaniale mieć naprawdę czysty sposób na zrobienie tego. –

+1

Jako kontynuację, zdaję sobie sprawę, że zastrzeżenia 1 i 3 zostaną prawdopodobnie zaadresowane w aktualizacji phpDocumentor, aby lepiej radzić sobie z cechami, jeśli w rzeczywistości nie zostały już zaadresowane. Ale nie widzę rozwiązania przychodzącego do 2. Jawnie wymaganie, aby klasa klienta wiedziała, że ​​Config musi być używana, jeśli Logger jest używany, będzie się bałaganić bez względu na to, co myślę. Widzę, że działa dobrze w niektórych ściśle określonych przypadkach, ale w tym przypadku, Config i Logger po prostu czują się do mnie podobni, ponieważ same powinny być "cegiełkami" wielokrotnego użytku. Z drugiej strony, jeśli jest to mało prawdopodobne, klasa kiedykolwiek użyje Logger, ale nie skonfiguruje ... –

+0

Czy znasz jakieś IDE, które przepływa pomiędzy cechami? Kiedy klikam na $ this-> getConfig, IDE przechodzi do abstrakcyjnej definicji, a nie do definicji metody w funkcji Config. Może phpdoc, który pomaga IDE to zrobić? – corretge

Powiązane problemy