2012-10-21 10 views
18

Przykład # 2 z podręcznika PHP http://php.net/manual/en/language.oop5.traits.php stanyPHP cecha: czy istnieje właściwy sposób, aby zapewnić klasę używając cechę rozciąga się super klasy, która zawiera pewne metody?

<?php 
class Base { 
    public function sayHello() { 
     echo 'Hello '; 
    } 
} 

trait SayWorld { 
    public function sayHello() { 
     parent::sayHello(); 
     echo 'World!'; 
    } 
} 

class MyHelloWorld extends Base { 
    use SayWorld; 
} 

$o = new MyHelloWorld(); 
$o->sayHello(); 
?> 

Jest to prawidłowy kod, ale nie jest bezpieczny w użyciu parent:: w tym kontekście. Powiedzmy, że napisałem moje własne klasy „Hello World”, która nie dziedziczy żadnych innych klas:

<?php 
class MyOwnHelloWorld 
{ 
    use SayWorld; 
} 
?> 

Kod ten nie przyniesie żadnych błędów, dopóki nie wywołać metodę sayHello(). To jest złe.

Z drugiej strony, jeśli cecha wymaga użycia pewnej metody: , mogę napisać tę metodę jako abstrakcyjną, i jest to dobre, ponieważ zapewnia, że ​​cecha jest poprawnie używana podczas kompilacji. Ale to nie ma zastosowania do klas dominujących:

<?php 
trait SayWorld 
{ 
    public function sayHelloWorld() 
    { 
     $this->sayHello(); 
     echo 'World!'; 
    } 

    public abstract function sayHello(); // compile-time safety 

} 

Więc moje pytanie brzmi: Czy istnieje sposób, aby zapewnić (w czasie kompilacji, a nie przy starcie), że klasa, która wykorzystuje pewną cechę będzie miał parent::sayHello() metody?

+0

Co możesz zrobić, to dodać metodę abstrakcyjną, aby zmusić klasę, używając tej cechy, do posiadania wymaganej metody. w ten sposób uniezależnia się od hierarchii klas (idea za cechami: P) – Jarry

Odpowiedz

4

Nie, nie ma. W rzeczywistości ten przykład jest bardzo zły, ponieważ celem wprowadzenia cech było wprowadzenie tej samej funkcjonalności do wielu klas bez polegania na dziedziczeniu, a użycie parent nie tylko wymaga posiadania klasy rodzica, ale także powinno mieć określoną metodę.

Na notatki bocznej, połączenia parent nie są sprawdzane w czasie kompilacji, można zdefiniować prostą klasę, która nie rozszerza niczego z wywołania nadrzędnego w jego metodach, i zadziała dopóki jedna z tych metod nie zostanie wywołana.

+0

Dzięki za to. Jestem świadomy, że proste klasy nie sprawdzają połączeń nadrzędnych w czasie kompilacji, ale wciąż jest to poprawny kod. Moje zawsze tak mądre IDE może wykombinować wywołanie nadrzędne, co oznacza, że ​​po przejściu z PHP na język o silnym typie nadal będzie to poprawny kod. To pozwala mi spać w nocy. Ale jeśli chodzi o wielokrotne dziedziczenie, nie jestem świadomy żadnego innego języka, który nie może tego zrobić. Pojęcie cech w Scali umożliwia dziedziczenie w obie strony. Klasy C++ i Python mogą dziedziczyć wiele "rodziców". –

+0

Sądzę więc, że szukam sposobu na obejście ograniczeń PHP, które sprawiłyby, że mój kod byłby trochę bezpieczniejszy. –

0

Myślę, że jest to sposób całkowicie pozbawiony cech:

class BaseClass 
{ 
    public function sayHello() { 
      echo 'Hello '; 
    } 
} 

class SayWorld 
{ 
    protected $parent = null; 

    function __construct(BaseClass $base) { 
     $this->parent = $base; 
    } 

    public function sayHelloWorld() 
    { 
     $this->parent->sayHello(); 
     echo 'World!'; 
    } 
} 

class MyHelloWorld extends Base { 
    protected $SayWorld = null; 

    function __construct() { 
     $this->SayWorld = new SayWorld($this); 
    } 

    public function __call (string $name , array $arguments) { 
     if(method_exists($this->SayWorld, $name)) { 
      $this->SayWorld->$name(); 
     } 
    } 
} 
2

Można sprawdzić, czy $ ta rozciąga konkretną klasę lub realizuje specyficzny interfejs:

interface SayHelloInterface { 
    public function sayHello(); 
} 

trait SayWorldTrait { 
    public function sayHello() { 
     if (!in_array('SayHello', class_parents($this))) { 
      throw new \LogicException('SayWorldTrait may be used only in classes that extends SayHello.'); 
     } 
     if (!$this instanceof SayHelloInterface) { 
      throw new \LogicException('SayWorldTrait may be used only in classes that implements SayHelloInterface.'); 
     } 
     parent::sayHello(); 
     echo 'World!'; 
    } 
} 

class SayHello { 
    public function sayHello() { 
     echo 'Hello '; 
    } 
} 

class First extends SayHello { 
    use SayWorldTrait; 
} 

class Second implements SayHelloInterface { 
    use SayWorldTrait; 
} 

try { 
    $test = new First(); 
    $test->sayHello(); // throws logic exception because the First class does not implements SayHelloInterface 
} catch(\Exception $e) { 
    echo $e->getMessage(); 
} 

try { 
    $test = new Second(); 
    $test->sayHello(); // throws logic exception because the Second class does not extends SayHello 
} catch(\Exception $e) { 
    echo $e->getMessage(); 
} 
1

Etap kompilacji PHP jedynie tworzy kod bajtowy. Cała reszta jest wykonywana w czasie wykonywania, w tym podejmowania polimorficznych decyzji. Tak więc kod jak poniżej kompiluje:

class A {} 
class B extends A { 
    public function __construct() { 
     parent::__construct(); 
    } 
} 

ale wieje, kiedy run:

$b = new B; 

W ten sposób nie może mieć ściśle parent sprawdzając w czasie kompilacji. Najlepsze, co możesz zrobić, to odłożyć to na czas tak szybko, jak to możliwe. Jak inne odpowiedzi wykazały, że to możliwe, aby to zrobić wewnątrz cecha metody z instanceof. Ja osobiście wolę przy użyciu typu podpowiedzi, kiedy wiem, że metoda wymaga cecha danego zamówienia.

Wszystko to sprowadza się do podstawowego celu cechami: skompilować skopiować czas i pasty. to jest to! Cechy nic nie wiedzą o kontraktach. Nic nie wiemy o polimorfizmie. Po prostu zapewniają mechanizm ponownego użycia. W połączeniu z interfejsami, są dość mocne, choć nieco rozwlekły.

W rzeczywistości first academic discussion of traits obnaża się pomysł, że cechy mają być „czysty” w tym, że nie mają wiedzy na temat obiektu wokół nich:

Cechą jest zasadniczo grupa czystych metod służy jako element konstrukcyjny klas i jest prymitywną jednostką ponownego użycia kodu. W tym modelu klasy składają się z zestawu cech, określając kod kleju, który łączy cechy i uzyskuje dostęp do niezbędnego stanu.

"If Traits Weren't Evil, They'd Be Funny" ładnie podsumowuje punkty, pułapki i opcje. Nie podzielam witriolu autora za cechy: używam ich, gdy ma to sens i zawsze w parze z interface. To, że przykład w dokumentacji PHP zachęca do złego zachowania, jest niefortunne.

+1

W czasie, gdy zadałem to pytanie, w ogóle przestałem używać cech. Próbuję przepisać mój stary kod, gdy tylko to możliwe, aby usunąć cechy. Możesz powiedzieć, że dorastałem. Czasami są przydatne, ale tylko w nielicznych sytuacjach. –

Powiązane problemy